1. 基本类型
ECMAScript标准定义了以下7种基本数据类型:
- 数值(number)
- 字符串(string)
- 布尔值(boolean)
- null
- undefined
- symbol(ES6新增,表示一个独一无二的值)
- bigint(ES10新增,可以用任意精度表示整数,安全地存储和操作大整数)
2. 对象(引用类型)
对象(object,也就是一般所说的引用类型)包括:
- 狭义的对象(也就是通常所指的对象)
- 数组
- 函数(function)
3. typeof运算符
typeof运算符作用于数值、字符串、布尔值、undefined、symbol类型的值和bigint类型的值时,返回表示各自类型的字符串,而作用于null时则返回"object"
:
typeof 1 // "number"
typeof 'abc' // "string"
typeof true // "boolean"
typeof undefined // "undefined"
let a = Symbol('foo') // Symbol(foo)
typeof a // "symbol"
let b = BigInt(123) // 123n
typeof b // "bigint"
typeof null // "object"
对于对象和数组,typeof运算符的结果都是"object"
,而对于函数结果则是"function"
:
typeof {a:1, b:2} // "object"
typeof [1, 2, 3] // "object"
typeof (() => {}) // "function"
概括起来,typeof
运算符可以判断一个值是否是基本类型(null
除外)以及函数,但无法区分对象、数组和null
。
4. instanceof运算符
instanceof
用于判断一个构造函数(或者称之为类)的原型对象是否在一个对象的原型链上:
[1, 2, 3] instanceof Array // true
[1, 2, 3] instanceof Object // true
(() => {}) instanceof Function // true
(() => {}) instanceof Object // true
({a:1, b:2}) instanceof Object // true
因此,可以使用instanceof
运算符判断一个引用类型的值是对象还是数组。
在ECMAScript标准中,Object
是所有类的根类,也就是所有的对象都可以通过原型链追溯到Object.prototype
,所以对象、数组和函数通过instanceof
运算符作用于Object
的结果都是true
。
需要注意的是,当instanceof
运算符左侧不是一个广义的对象而是一个基本类型值时,结果都为false
:
123 instanceof Number // false
'abc' instanceof String // false
true instanceof Boolean // false
Symbol('foo') instanceof Symbol // false
BigInt(123) instanceof BigInt // false
undefined instanceof Object // false
null instanceof Object // false
可以看到,null
很特殊:typeof
运算符作用于null
的结果是"object"
,但null
却不是Object
的实例。
这一点可以借助原型链解释:Object.prototype
并不是原型链的终点。作为一个对象,Object.prototype
也具有__proto__
属性,而Object.prototype.__proto__
恰好就是null
:
Object.prototype.__proto__ === null // true
因此,null
可以看做是一个特殊的对象(地址为空),因此typeof
运算符作用于null
的结果为"object"
,但它本身不是Object
构造函数产生。
5. 类型转换
5.1 显式类型转换
显式类型转换即通过Number()
、String()
和Boolean()
方法将其参数转换为数值、字符串和布尔值。
5.1.1 Number()方法
// 数值(包括bigint)转换为数值本身
Number(123) // 123
Number(BigInt(1e50)) // 1e50
// 字符串:能被解析为数值则转换为数值,否则为NaN
Number('123') // 123
Number('123abc') // NaN
// 布尔值
Number(true) // 1
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0
// 传入symbol类型的值则报错
Number(Symbol(123)) // Uncaught TypeError: Cannot convert a Symbol value to a number
传入引用类型的值,转换规则如下:
- 首先调用变量的
valueOf()
方法,如果返回值是基本类型,则对返回值应用Number()
方法 - 否则,继续调用
toString()
方法,如果返回值是基本类型,则对返回值应用Number()
方法 - 否则,报错
对象、数组和函数的valueOf()
方法返回其自身:
({a:1}).valueOf() // {a: 1}
[1, 2, 3].valueOf() // [1, 2, 3]
(() => {}).valueOf() // () => {}
// 原生函数
Number.valueOf() // ƒ Number() { [native code] }
toString()
返回值如下:
({a:1}).toString() // "[object Object]"
[1, 2, 3].toString() // "1,2,3"
(() => {}).toString() // "() => {}"
// 原生函数
Number.toString() // "function Number() { [native code] }"
因此,对象、数组和函数传入Number()
方法的返回值都是NaN
,除非是只含有一个数值元素的数组(转换结果是这个数值元素)。
5.1.2 String()方法
基本类型值传入String()
方法,返回值为传入的参数组成的字符串:
String(123) // 123
String('abc') // "abc"
String(true) // "true"
String(null) // "null"
String(undefined) // "undefined"
String(Symbol('foo')) // "Symbol(foo)"
String(BigInt(123) // "123"
对于引用类型的变量:
- 先调用其
toString()
方法,如果返回值是基本类型,则对该值使用String()
方法转换为字符串 - 否则,继续调用
valueOf()
方法,如果返回值是基本类型,则对该值使用String()
方法转换为字符串 - 否则,报错
除非重写toString()
方法,对象、数组和函数传入String()
方法的结果如下:
String({a:1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
String(() => {}) // "() => {}"
// 原生函数
String(String) // "function String() { [native code] }"
5.1.3 Boolean()方法
以下值的转换结果为false
:
false
-
0
(包括+0
和-0
) -
''
或者""
(空字符串) null
undefined
NaN
其他值的转换结果均为true
。
5.2 隐式类型转换
隐式类型转换发生在不同类型的值进行四则运算以及条件判断语句中,JavaScript引擎会对值自动使用Number()
、String()
或者Boolean()
方法。
- 对于四则运算,当使用
+
时,会对参与运算的值自动地使用String()
进行转换(但如果是在一个值前面加一个+
,则是使用Number()
方法进行转换)。
对于其他算数运算符,自动使用Number()
进行转换,然后对转换后的结果进行计算。
+
运算符会导致一些看起来很奇怪的运算结果:
[] + [] // ""
[] + {} // "[object Object]"
{} + [] // 在Chrome下为0,因为{}被认为是一个空的区块
({} + []) // "[object Object]"
{} + {} // "[object Object][object Object]"(这点就很疑惑...又把{}视为空对象了)
- 对于条件判断语句,如
if('123')
,则是对语句中的表达式使用Boolean()
转换为一个布尔值。