1 数据类型简介
JavaScript 语言的每一个值,都属于某一种数据类型。 JavaScript 的数据类型,共有六种。ES6 又新增了第七种 Symbol 类型的值,本教程不涉及。
undefined
null
- bool:原始类型
- 数值:原始类型
- 字符串:原始类型
- 对象:合成类型
JavaScript 有三种方法,可以确定一个值到底是什么类型。
- typeof
- instanceof
- Object.prototype.toString
// 错误的写法
if (v) {
// ...
}
// ReferenceError: v is not defined
// 正确的写法
if (typeof v === "undefined") {
// ...
}
typeof 对象返回 object 。空数组的类型也是 object ,这表示在 JavaScript 内部,数组本质上只是一种特殊的对象。这里顺便提一下, instanceof 运算符可以区分数组和对象。
var o = {};
var a = [];
o instanceof Array // false
a instanceof Array // true
null
的类型是 object
,这是由于历史原因造成的。 1995 年的 JavaScript 语言第一版,只设计了五种数据类型,即对象、整数、浮点数、字符串和布尔值,没考虑 null
,只把它当作 object
的一种特殊值。后来 null
独立出来,作为一种单独的数据类型,为了兼容以前的代码, typeof null
返回 object
就没法改变了。
typeof null // "object"
2 null 和 undefined
null
与 undefined
都可以表示“没有”,含义非常相似。将一个变量赋值为 undefined
或 null
,老实说,语法效果几乎没区别。
var a = undefined;
// 或者
var a = null;
上面代码中,变量 a
分别被赋值为 undefined
和 null
,这两种写法的效果几乎等价。
在 if
语句中,它们都会被自动转为 false
, ==
甚至直接报告两者相等。
if (!undefined) {
console.log('undefined is false');
}
// undefined is false
if (!null) {
console.log('null is false');
}
// null is false
undefined == null
// true
从上面代码可见,两者的行为是何等相似!谷歌公司开发的 JavaScript 语言的替代品 Dart 语言,就明确规定只有 null
,没有 undefined
!
既然含义与用法都差不多,为什么要同时设置两个这样的值,这不是无端增加复杂度,令初学者困扰吗?这与历史原因有关。
1995 年 JavaScript 诞生时,最初像 Java 一样,只设置了 null
表示”无”。根据 C 语言的传统, null
可以自动转为 0
。
Number(null) // 0
5 + null // 5
上面代码中, null
转为数字时,自动变成0。
但是, JavaScript 的设计者 Brendan Eich ,觉得这样做还不够。首先,第一版的 JavaScript 里面, null
就像在 Java 里一样,被当成一个对象, Brendan Eich 觉得表示“无”的值最好不是对象。其次,那时的 JavaScript 不包括错误处理机制, Brendan Eich 觉得,如果 null
自动转为 0 ,很不容易发现错误。
因此,他又设计了一个 undefined
。区别是这样的: null
是一个表示“空”的对象,转为数值时为 0
; undefined
是一个表示”此处无定义”的原始值,转为数值时为 NaN
。
Number(undefined) // NaN
5 + undefined // NaN
对于 null
和 undefined
,大致可以像下面这样理解。
null
表示空值,即该处的值现在为空。调用函数时,某个参数未设置任何值,这时就可以传入 null
,表示该参数为空。比如,某个函数接受引擎抛出的错误作为参数,如果运行过程中未出错,那么这个参数就会传入 null
,表示未发生错误。
undefined
表示“未定义”,下面是返回 undefined
的典型场景。
var i;
i // undefined
function f(x) {
return x;
}
f() // undefined
var o = new Object();
o.p // undefined
function f() {}
f() // undefined
3 bool
转换规则是除了下面六个值被转为 false ,其他值都视为 true 。注意,空数组 []
和空对象 {}
都是 true
。
undefined
null
false
0
NaN
""
或''
4 数值
4.1 大小和精度
IEEE 754 规定, JavaScript 浮点数的 64 个二进制位左起依次是:
- 1 正负符号
- 2~12 指数部分,决定大小
- 13~64 小数部分,即有效数字,决定精度
指数部分一共有 11 个二进制位,范围 -1023~1024 。
IEEE 754 规定,小数部分第 1 位默认 1 且不保存。有效数字总是 1.xx...
的形式,小数部分只保存最多 52 位的 xx..
,实际有效数字可达 53 位。 253 是一个 16 位的十进制数,因此小于 16 位的十进制数都不必担心。超出有效数字的部分用 0 替代。
Math.pow(2, 53)
// 9007199254740992
9007199254740992111
// 9007199254740992000
4.2 表示方法
小数点前的数字多于 21 位,或小数点后的零多于 5 个时, JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示。
4.3 正零和负零
JavaScript 内部实际上存在 2 个 0 :一个是 +0 ,一个是 -0 。区别就是 64 位浮点数表示法的符号位不同。它们是等价的。几乎所有场合,正零和负零都会被当作正常的 0 。唯一有区别的场合是, +0 或 -0 当作分母,返回的值是不相等的,非零除以正零得到 +Infinity ,非零除以负零得到 -Infinity 。
4.4 溢出
- 如果一个数的绝对值大于等于 21024,就会发生“正向溢出”,返回 Infinity 。
- 如果一个数的绝对值小于等于 2-1075 ,指数部分最小值 -1023 ,加上小数部分的 52 位,就会发生“负向溢出”,返回 0 。
连数值正向溢出、负向溢出和被 0 除这种事情 JavaScript 都不报错,可见单纯的数学运算几乎没有可能出现任何错误。
4.5 NaN
Infinity 大于一切数值,除了 NaN ; -Infinity 小于一切数值,除了 NaN 。
NaN (Not a Number) 是 JavaScript 的特殊值,表示“非数字”。需要注意的是, NaN 不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于 Number ,使用 typeof 可以鉴别。
NaN 不等于任何值,包括它本身。数值与 NaN 比较均返回 false 。 NaN 在布尔运算时被当作 false 。
4.6 相关方法
4.6.1 parseInt()
parseInt('123') // 123
parseInt(' 81') // 81
parseInt(1.23) // 1
parseInt('1.23') // 1
parseInt('8a') // 8
parseInt('12**') // 12
parseInt('12.34') // 12
parseInt('15e2') // 15
parseInt('15px') // 15
parseInt('abc') // NaN
parseInt('.3') // NaN
parseInt('') // NaN
parseInt('+') // NaN
parseInt('+1') // 1
parseInt('0x10') // 16
parseInt('011') // 11
parseInt(1000000000000000000000.5) // 1
parseInt('1e+21') // 1
parseInt(0.0000008) // 8
parseInt('8e-7') // 8
parseInt('1000', 10) // 1000
parseInt('1000', 2) // 8
parseInt('1000', 8) // 512
parseInt('10', 1) // NaN
parseInt('10', 37) // NaN
parseInt('10', 0) // 10
parseInt('10', null) // 10
parseInt('10', undefined) // 10
4.6.2 parseFloat()
parseFloat('3.14') // 3.14
parseFloat('314e-2') // 3.14
parseFloat('0.0314E+2') // 3.14
parseFloat('3.14more non-digit characters') // 3.14
parseFloat('\t\v\r12.34\n ') // 12.34
parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat(true) // NaN
Number(true) // 1
parseFloat(null) // NaN
Number(null) // 0
parseFloat('') // NaN
Number('') // 0
parseFloat('123.45#') // 123.45
Number('123.45#') // NaN
4.6.3 isNaN()
isNaN(NaN) // true
isNaN(123) // false
isNaN('Hello') // true
isNaN(Number('Hello')) // true
isNaN({}) // true
isNaN(Number({})) // true
isNaN(['xzy']) // true
isNaN(Number(['xzy'])) // true
isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false
function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}
function myIsNaN(value) {
return value !== value;
}
4.6.4 isFinite()
isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true
5 字符串
5.1 定义
单双引号均可。由于 HTML 语言的属性值使用双引号,所以很多项目约定 JavaScript 语言的字符串只使用单引号,本教程遵守这个约定。当然,只使用双引号也完全可以。重要的是坚持使用一种风格,不要一会使用单引号表示字符串,一会又使用双引号表示。
如果长字符串必须分成多行,可以在每一行的尾部使用反斜杠。
5.2 转义
'\251' // "©"
'\xA9' // "©"
'\u00A9' // "©"
5.3 长度
var s = 'hello';
s.length // 5
s.length = 3;
s.length // 5
s.length = 7;
s.length // 5
字符串的长度属性无法改变,操作会默默地失败。
5.4 字符集
JavaScript 使用 Unicode 字符集。JavaScript 引擎内部,所有字符都用 Unicode 表示。每个字符在 JavaScript 内部以 16 位(即 2 字节)的 UTF-16 格式储存。但是 UTF-16 有两种长度:码点在 U+0000 到 U+FFFF 之间的字符,长度为 16 位(即 2 字节),码点在 U+10000 到 U+10FFFF 之间的字符,长度为 32 位(即 4 字节)。因为 JavaScript 第一版发布的时候, Unicode 的码点只编到 U+FFFF ,因此 2 字节足够表示。后来 Unicode 纳入的字符越来越多,出现了 4 字节字符,而 JavaScript 的标准此时已经定型,导致无法识别 4 字节的字符,也就是说, JavaScript 返回的字符串长度不可信。
5.5 Base64
Base64 就是一种编码方法,可以将任意值转成 0~9 、 A~Z 、 a~z 、 + 和 / 这 64 个字符组成的可打印字符。 ASCII 码 0 到 31 的符号都无法打印出来,这时就可以使用 Base64 编码,将它们转码。有时需要以文本格式传递二进制数据,也可以使用 Base64 编码。
btoa()
编码atob()
解码
var string = 'Hello World!';
btoa(string) // "SGVsbG8gV29ybGQh"
atob('SGVsbG8gV29ybGQh') // "Hello World!"
btoa('你好') // 报错,不适合非 ASCII 码的字符
btoa(encodeURIComponent('你好')) // "JUU0JUJEJUEwJUU1JUE1JUJE"
decodeURIComponent(atob('JUU0JUJEJUEwJUU1JUE1JUJE')) // "你好"