JavaScript的类型、值和变量
JavaScript的数据类型
数据类型(type):能够表示并操作的值得类型
变量(variable):一个值的符号名称,通过名称获得对值的引用
原始类型(primitive type)
数字
字符串
布尔值
对象类型(object type)
特殊的原始值
null(空)
undefined(未定义)
数字
JavaScript不区分整数值和浮点数,所有数字均用浮点数(IEEE754)表示
整型
十进制:0、9、314124
十六进制(以0x或0X为前缀):0xff(255)
八进制:部分支持,ES6严格模式不支持
浮点型
含有小数点:3.14、.475
指数计数法:9.234e1231、2.31E-1423523
运算
加(+)、减(-)、乘(*)、除(/)、求余(%)
特殊情况
溢出(overflow):数字运算结果超过JavaScript所能表示的数字上限,结果为一个无穷大值,用
Infinity
表示1
2
3
41 / 0 // => Infinity
-1 / 0 // => -Infinity
1 + Infinity // => Infinity
Infinity + Infinity // => InfinityNumber.MAX_VALUE + 1结果并非Infinity(1.7976931348623157e+308)。参考为什么在 js 中 Number.MAX_VALUE + 1 不是 Infinity?
下溢(underflow):运算结果无限接近0并比JavaScript能表示的最小值还小,此时JavaScript返回0。当一个负数发生下溢时,返回-0
1
20 === -0 // => true
1 / 0 === 1 / -0 // => false非数字(NaN)
1
2
3
40 / 0 // => NaN
Infinity - Infinity // => NaN
Infinity / Infinity // => NaN
Math.sqrt(-1) // => NaNNaN
与任何值都不相等,包括自身,无法用x == NaN
判断。当且仅当x为NaN
,x != x
为true1
2var x = NaN;
console.log(x != x); // => true
二进制浮点数和四舍五入错误
用JavaScript使用实数时,常常只是真实值的一个近似表示
浮点数转化为二进制可能会产生无限循环小数,由于IEEE754尾数位数限制,需要将后面多余的位截掉,由于指数位数不相同,运算时需要对阶运算,这部分也可能产生精度损失
0.1 -> 0.0001100110011001…(无限循环)
0.2 -> 0.0011001100110011…(无限循环)
1 | var x = 0.3; |
可以通过转化为整数解决这个问题
1 | (10 * x - 10 * y) == (10 * y - 10 * z); // => true |
日期和时间
1 | var then = new Date(2018, 0, 1); // => 2018年1月1日 |
获取当月第一天
1
new Date(year, month - 1, 1)
获取上月最后一天
1
new Date(year, month - 1, 0)
字符串
由16位值组成的不可变的有序序列。每个字符通常来自Unicode字符集
字符串直接量
由单引号或双引号括起来的字符序列。
由单引号定界的字符串中可以包含双引号,由双引号定界的字符串中可以包含单引号。
1
2
3
4'' // => 空字符串
'abcdefg' // => 字符串abcdefg
'3.14' // => 字符串3.14
'name = "tfcx"' // => 字符串(name = "tfcx")字符串直接量可以拆分成数行,每行必须以反斜线
\
结束,反斜线和行结束符都不算字符串直接量的内容1
2
3
4var str = 'tong\
hao'
alert(str); // tong hao
alert(str.length); // 9换行时行前空格也算作字符
希望字符串另起一行可以加
\n
,\n
占一个字符1
2
3
4var str = 'tong\nhao'
alert(str); // => tong
// hao
alert(str.length); // => 8
字符串的使用
字符串连接
+
用于字符串表示将第二个字符串拼接在第一个之后1
'XJ' + 'TU' // => XJTU
连接数字和字符串时,从左往右计算,数字转换为字符串
1
2'asd' + 1 + 2 // => asd12
1 + 2 + 'asd' // => 3asd其他方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45var str = 'hello, world';
str.length // => 12
str.charAt(0) // => h
str.charAt(str.length - 1) // => d\
str.toUpperCase() // => HELLO, WORLD
str.toLowerCase() // => hello, world
str.replace('h', 'H') // => Hello, world
/**
* String.substring(from, to) 返回字符串子串
* @param {number} from - 非负整数,指定要提取的子串的第一个字符在string中的位置
* @param {number} to - 非负整数,比要提取的子串的最后一个字符的位置大1;若省略则子串持续到末尾
* @returns {String} - 返回新的字符串,为原字符串from到to-1的副本
*/
str.substring(1, 4) // => ell
str.substring(7) // => world
/**
* String.slice(start, end) 提取一个子串
* @param {number} start - 切片开始的索引,如果为负,则从尾部开始计算
* @param {number} end - 紧跟着切片结尾的字符串索引;如果不指定,则切片将包括从start开始到结尾的所有字符;如果为负,则从尾部开始计算
* @returns {String} - 返回新的字符串,为原字符串start到end但不包括end的所有字符的副本
*/
str.slice(2, 6) // => llo,
str.slice(9) // => rld
str.slice(-4, -1) // => orl
str.slice(4, -1) // => o, worl
/**
* String.split(delimiter, limit) 将一个字符串切分成一个由字符串组成的数组
* @param delimiter - string切分处的字符串或正则表达式
* @param limit {number} - 可选,指定已返回数组的最大长度
* @returns {Array} - 一个由字符串组成的数组
*/
str.split(',') // => ['hello', ' world']
str.split('o', 2) // => ['hell', ', w']
/**
* String.indexOf(substring, start) 搜索一个子串
* @param substring {String} - 在String中搜索的子串
* @param start {number} - 可选的整数,指定该次搜索在字符串中的开始位置,合法值为0到String.length - 1;若省略,则从头开始
* @returns {number} - 返回substring第一次出现的位置,若没找到则返回-1
*/
str.indexOf('ll') // => 2
str.indexOf('lluj') // => -1在JavaScript中字符串都是固定不变的,类似
replace()
等方法都返回新字符串,原字符串本身不发生改变;字符串也可看做只读数组,用方括号访问单个字符
布尔值
布尔值类型只有true
和false
,任意JavaScript的值都可以转换为布尔值
undefined
,null
,0
,-0
,NaN
,''
会被转换为false
;所有其他值都会转换成true
null和undefined
null
是JavaScript的关键字,常用来描述“空值”。null
是一个特殊的对象值,通常认为null
是它自有类型的唯一一个成员,表示数字、字符串和对象是“无值”的。1
typeof null // => object
undefined
是预定义的全局变量,不是关键字,ES5中是只读的1
typeof undefined // => undefined
undefined
是变量的一种取值,表示变量没有初始化;如果要查询对象的属性或数组元素的值时返回
undefined
则说明这个属性或者元素不存在;如果函数没有返回任何值,则返回
undefined
;引用没有提供实参的函数形参的值也只会得到
undefined
1
2null == undefined // => true
null === undefined // => false
全局对象
全局对象的属性是全局定义的符号,JavaScript程序可以直接使用。当JavaScript解释器启动时(或任何web浏览器加载新页面时),将创建一个新的全局对象,并给他一组定义的初试属性:
全局属性:
undefined
、Infinity
、NaN
等全局函数:
isNaN()
、parseInt()
、eval()
等构造函数:
Date()
、RegExp()
、String()
、Object()
、Array()
等全局对象:
Math
、JSON
等
在客户端JavaScript中,Window对象充当了全局对象。
当初次创建时,全局对象定义了JavaScript中所有的预定义全局值。这个特殊对象同样包含了为程序定义的全局值。如果代码声明了一个全局变量,这个全局变量就是全局对象的一个属性。
包装对象
存取字符串、数字或布尔值的属性时创建的临时对象称作包装对象。
例:
1 | var str = 'hello, world'; |
当引用字符串str
的属性,JavaScript就会将字符串值通过调用new String(s)
的方式转换成对象,这个对象继承了字符串的方法,并被用来处理属性的引用。一旦属性引用结束,这个新创建的对象就会销毁。
1 | var str = 'test'; |
修改只发生在临时对象身上,而临时对象并未继续保留下来
1 | var str = 'test'; |
不可变的原始值和可变的对象引用
原始值:数字、字符串、布尔值、undefined
、null
对象:数组、函数等
原始值
原始值用任何方法都不能更改;字符串的所有方法均返回一个新的字符串
1
2
3var str = 'test';
var str1 = str.toUpperCase();
console.log(str); // => 'test'原始值的比较是值的比较:值相等的时候才相等;对于字符串,当且仅当长度相等且每个索引的字符串都相等时才相等。
对象
对象的值是可修改的
1
2
3var arr = [1, 2, 3];
arr[1] = 5;
console.log(arr); // => [1, 5, 3]对象的比较不是值的比较,即使两个对象包含同样的属性及相同的值,他们也不相等;各个索引元素完全相同的两个数组也不相等
1
2
3var obj1 = { x: 1 },
obj2 = { x: 1 };
console.log(obj1 === obj2); // => fasle对象是引用类型(reference type),对象的比较均是引用的比较,当且仅当它们引用同一个基对象时才相等
1
2
3
4
5var arr1 = [1, 2, 3];
var arr2 = arr1;
console.log(arr1 === arr2); // => true
arr2[2] = 9;
console.log(arr1[2]); // => 9想得到一个数组或对象的副本,必须显式复制对象的每个属性或数组的每个元素
1
2
3
4
5var arr1 = [1, 2, 3];
var arr2 = [];
for (var i = 0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}如果想比较两个单独的对象或者数组,则必须比较它们的属性或元素
1
2
3
4
5
6
7
8
9
10
11function euqalArrays(arr1, arr2) {
if (arr1.length != arr2.length) {
return false;
}
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]) {
return false;
}
}
return true;
}
类型转换
原始值的转换
值(转换为) | 字符串 | 数字 | 布尔值 | 对象 |
---|---|---|---|---|
undefined | ‘undefined’ | NaN | false | throw TypeError |
null | ‘null’ | 0 | false | throw TypeError |
true | ‘true’ | 1 | new Booleaan(true) | |
false | ‘false’ | 0 | new Boolean(false) | |
‘’(空字符串) | 0 | false | new String(‘’) | |
‘1.2’ | 1.2 | true | new String(‘1.2’) | |
‘one’ | NaN | true | new String(‘one’) | |
0 | ‘0’ | false | new Number(0) | |
-0 | ‘0’ | false | new Number(-0) | |
NaN | ‘NaN’ | false | new Number(NaN) | |
Infinity | ‘Infinity’ | true | new Number(Infinity) | |
-Infinty | ‘-Infinity’ | true | new Number(-Infinity) |
以数字表示的字符串转换为数字时,允许开头或结尾带空格,但开头或结尾处的任意非空格字符以及数字中间的任意字符会造成字符串转换为NaN
1 | var str = ' 3.14 ', |
显式类型转换
最简单的方法是使用
Boolean()
、Number()
、String()
或Object()
函数除了
null
或undefined
之外的任何值都具有toString()
方法,这个方法的执行结果通常和String()
方法的返回结果一致JavaScript中的某些运算符会做隐式的类型转换
1
2
3x + '' // => String(x)
+x // => Number(x)
!!x // => Boolean(x);Number
类转换为String
的方法toString()
方法可以接受表示转换基数的可选参数,如果不指定,则默认十进制1
2
3
4var n = 15;
n.toString(2) // => '1111'
n.toString(8) // => '17'
n.toString(16) // => 'f'toFixed()
根据小数点后指定位数将数字转换为字符串1
2
3
4var n = 3.141;
n.toFixed() // => 3
n.toFixed(2) // => 3.14
n.toFixed(4) // => 3.1410toExponential()
使用指数计数法将数字转换为指数形式的字符串,其中小数点前只有一位,小数点后的位数由参数指定1
2
3
4var n = 12345.6789;
n.toExponential() // => '1.23456789e+4'
n.toExponential(2) // => '1.23e+4'
n.toExponential(9) // => '1.234567890e+4'toPrecision()
根据指定的有效数字位数将数字转换成字符串1
2
3
4var n = 12345.6789;
n.toPrecision(3) // => '1.23e+4'
n.toPrecision(7) // => '12345.68'
n.toPrecision(10) // => '12345.67890'
String
类转换为Number
的方法通过
Number()
方法只能基于十进制,并且不能出现非法的尾随字符1
2var str = '3.14qwe'
Number(str) // => NaNparseInt()
方法只解析整数,如果字符串前缀是0x
或0X
将被认为是十六进制;parseFloat()
可以解析整数和浮点数。调用时会跳过任意数量的前导空格,尽可能解析更多数字字符,并忽略后面的内容。如果第一个非空格字符是非法字符,则返回NaN
1
2
3
4
5parseInt('3asd') // => 3
parseInt('-3.14') // => -3
parseInt('0xff') // => 255
parseFloat('3.14qwe') // => 3.14
parseFloat('qw12.324') // => NaNparseInt()
方法可以接受第二个可选参数,这个参数指定数字转换的基数,合法的取值范围是2~361
parseInt('1010', 2) // => 10
对象转换为原始值
对象到布尔值:所有的对象(包括数组和函数)都转换为
true
1
new Boolean(false) // 将被转换为true
对象到字符串和对象到数字:通过调用待转换对象的一个方法来完成的(这里提到的转换规则只适用于本地对象)
toString()
:作用是返回一个反映这个对象的字符串1
2
3
4
5{x: 1, y: 2}.toString() // => '[object Object]'
[1, 2, 3].toString() // => '1,2,3'
(function(x) { x++; }).toString() // => 'function (x) { x++; }'
new Date(2019, 1, 1).toString() // => 'Fri Feb 01 2019 00:00:00 GMT+0800 (CST)'
(/\d+/g).toString() // => '/\d+/g'valueOf()
:如果存在任意原始值,默认将对象转换为表示他的原始值;对象是复合值,默认的valueOf
方法简单的返回对象本身1
2
3[1, 2, 3].valueOf() // => [1,2,3]
{x:1, y:2}.valueOf() // => {x:1, y:2}
new Date(2019, 1, 1).valueOf() // => 1548950400000
对象到字符串的转换步骤
如果对象具有
toString()
方法,则调用这个方法;若返回一个原始值,JavaScript将这个值转换为字符串,并返回字符串;如果对象不具有
toString()
方法,或者不返回一个原始值,JavaScript将调用valueOf()
方法;若返回一个原始值,JavaScript将这个值转换为字符串,并返回字符串;否则JavaScript无法从
toString()
或valueOf()
获得一个原始值,将抛出类型错误异常
对象到数字的转换中,首先尝试
valueOf()
变量作用域
全局作用域与函数作用域
全局变量具有全局作用域;在函数内声明的变量只在函数体内有定义
在函数体内,局部变量的优先级高于同名的全局变量
1
2
3
4
5
6
7
8var scope = 'asd';
function f() {
var scope = 'qwe';
console.log(scope);
return scope;
}
f(); // => qwe
console.log(scope); // => asd声明局部变量时必须使用
var
1
2
3
4
5
6
7
8scope = 'asd';
function f() {
scope = 'qwe';
console.log(scope);
return scope;
}
f(); // => qwe
console.log(scope); // => qwe
函数作用域和声明提前
JavaScript没有块级作用域
函数作用域:变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都是有定义的
1
2
3
4
5
6
7
8
9
10
11function f(o) {
var i = o;
if (typeof o == 'object') {
var j = 0;
for (var k = 0; k < 10; k++) {
console.log(k); // => 0-9
}
console.log(k); // => 10
}
console.log(j); // => 0
}声明提前:JavaScript函数里声明的所有变量(不涉及赋值)都被提升到函数体的顶部
1
2
3
4
5
6var scope = 'asd';
function f() {
console.log(scope); // => undefined
var scope = 'qwe';
console.log(scope); // => qwe
}