显式数据类型转化
ES6 前,JavaScript 共有六种数据类型:Undefined
、Null
、Boolean
、Number
、String
、Object
。
原始值转布尔
我们使用 Boolean函数
将类型转换成布尔类型,在 JavaScript 中,只有 6 种值可以被转换成 false,其他都会被转换成 true。
// 当 Boolean 函数不传任何参数时,会返回 false。
console.log(Boolean()) // false
console.log(Boolean(false)) // false
console.log(Boolean(undefined)) // false
console.log(Boolean(null)) // false
console.log(Boolean(+0)) // false
console.log(Boolean(-0)) // false
console.log(Boolean(NaN)) // false
console.log(Boolean("")) // false
原始值转数字
我们可以使用 Number函数
将类型转换成数字类型。如果 Number 函数不传参数,返回 +0;如果有参数,隐式调用 ToNumber(value),ToNumber 则直接给了一个对应的结果表,表如下。但如果参数无法被转换为数字,则返回 NaN。
参数类型 | 结果 |
---|---|
Undefined | NaN |
Null | +0 |
Boolean | 如果参数是true,返回1;参数为false,返回+0 |
Number | 返回与之相等的值 |
String | 这段比较复杂,看例子 |
// 不传参
console.log(Number()) // +0
// 传参
- Undefined
console.log(Number(undefined)) // NaN
- Null
console.log(Number(null)) // +0
- Boolean
console.log(Number(true)) // 1
console.log(Number(false)) // +0
- String
console.log(Number("123")) // 123
console.log(Number("-123")) // -123
console.log(Number("1.2")) // 1.2
console.log(Number("000123")) // 123
console.log(Number("-000123")) // -123
console.log(Number("0x11")) // 17
console.log(Number("")) // 0
console.log(Number(" ")) // 0
console.log(Number("123 123")) // NaN
console.log(Number("foo")) // NaN
console.log(Number("100a")) // NaN
如果通过 Number 转换函数传入一个字符串,它会试图将其转换成一个整数或浮点数,而且会忽略所有前导的 0;如果有一个字符不是数字,结果都会返回 NaN。鉴于这种严格的判断,我们一般还会使用更加灵活的 parseInt
和 parseFloat
进行转换。
- parseInt 只解析整数。如果字符串前缀是 “0x” 或者”0X”,会将其解释为十六进制数。
console.log(parseInt("0xFF")) // 255 console.log(parseInt("0.1")) // 0
- parseFloat 则可以解析整数和浮点数。
console.log(parseFloat("3.14 abc")) // 3.14 console.log(parseFloat(".1")) // 0.1
- parseInt 和 parseFloat 都会跳过任意数量的前导空格,尽可能解析更多数值字符,并忽略后面的内容。如果第一个非空格字符是非法的数字直接量,将最终返回 NaN:
console.log(parseInt("3 abc")) // 3 console.log(parseInt("-12.34")) // -12
原始值转字符串
我们使用String函数
将类型转换成字符串类型。如果 String 函数不传参数,返回空字符串,如果**有参数,隐式调用 ToString(value)**。而 ToString 也给了一个对应的结果表,表如图:参数类型 结果 Undefined “undefined” Null “null” Bolean 如果参数是true,返回true。参数为false,返回false Number 又是比较复杂,可以看例子 String 返回与之相等的值 // 不传参 console.log(String()) // 空字符串 // 传参 - Undefined console.log(String(undefined)) // undefined - Null console.log(String(null)) // null - Boolean console.log(String(true)) // true console.log(String(false)) // false - Number console.log(String(0)) // 0 console.log(String(-0)) // 0 console.log(String(NaN)) // NaN console.log(String(Infinity)) // Infinity console.log(String(-Infinity)) // -Infinity console.log(String(1)) // 1
原始值转对象
原始值通过调用String()
、Number()
或者Boolean()
构造函数,转换为它们各自的包装对象,(null
和undefined
属于例外,当将它们用在期望是一个对象的地方都会造成一个类型错误 (TypeError) 异常,而不会执行正常的转换)。var b = new Number(a); console.log(typeof b); // object
对象转布尔值
所有对象(包括数组和函数)都转换为 true,对于包装对象也是这样。console.log(Boolean(new Boolean(false))) // true
对象转字符串和数字
对象到字符串 和 对象到数字 的转换都是通过调用待转换对象的一个方法来完成的。而 JavaScript 对象有两个不同的方法来执行转换,一个是toString
,一个是valueOf
。注意这个跟上面所说的 ToString 和 ToNumber 是不同的,这两个方法是真实暴露出来的方法。 - 当调用对象的 toString 方法时,其实调用的是 Object.prototype 上的 toString 方法。
- 然而 JavaScript 下的很多类根据各自的特点,定义了更多版本的 toString 方法。例如:
- 数组的 toString 方法将每个数组元素转换成一个字符串,并在元素之间添加逗号后合并成结果字符串。
console.log([].toString()) // "" console.log([0].toString()) // 0 console.log([1, 2, 3].toString()) // 1,2,3
- 函数的 toString 方法返回函数本身
console.log(({}).toString()) // [object Object] console.log((function(){var a = 1;}).toString()) // function (){var a = 1;}
- 日期的 toString 方法返回一个可读的日期和时间字符串。
var date = new Date(2010, 0, 1); console.log(date.toString()) // Fri Jan 01 2010 00:00:00 GMT+0800 (CST)
- RegExp 的 toString 方法返回一个表示正则表达式直接量的字符串。
console.log((/\d+/g).toString()) // /\d+/g
- 而另一个转换对象的函数是 valueOf,表示对象的原始值。默认的 valueOf 方法返回这个对象本身,数组、函数、正则简单的继承了这个默认方法,也会返回对象本身。日期是一个例外,它会返回它的一个内容表示: 1970 年 1 月 1 日以来的毫秒数。
var date = new Date(2017, 4, 21); console.log(date.valueOf()) // 1495296000000
- 对象转字符串
对象到字符串是如何转换的,其实就是 ToString 方法的对应表,只是这次我们加上 Object 的转换规则:
参数类型 | 结果 |
---|---|
Object | 1.primValue = ToPrimitive(input, String) 2.返回ToString(primValue) |
所谓的 ToPrimitive 方法,其实就是输入一个值,然后返回一个一定是基本类型的值。
- 语法:ToPrimitive(input[, PreferredType])
- 第一个参数是 input,表示要处理的输入值。如果传入的 input 是 Undefined、Null、Boolean、Number、String 类型,直接返回该值。
- 第二个参数是 PreferredType,非必填,表示希望转换成的类型,有两个值可以选,Number 或者 String。当不传入 PreferredType 时,如果 input 是日期类型,相当于传入 String,否则,都相当于传入 Number。
如果是 ToPrimitive(obj, Number),处理步骤如下:
- 如果 obj 为 基本类型,直接返回
- 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,JavaScript 抛出一个类型错误异常。
如果是 ToPrimitive(obj, String),处理步骤如下:
- 如果 obj为 基本类型,直接返回
- 否则,调用 toString 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,调用 valueOf 方法,如果返回一个原始值,则 JavaScript 将其返回。
- 否则,JavaScript 抛出一个类型错误异常。
初次总结:当我们用 String 方法转化一个值的时候,如果是基本类型,就参照 “原始值转字符” 这一节的对应表,如果不是基本类型,我们会将调用一个 ToPrimitive 方法,将其转为基本类型,然后再参照“原始值转字符” 这一节的对应表进行转换。
最终总结:对象转字符串(就是 Number() 函数)可以概括为:
- 如果对象具有 toString 方法,则调用这个方法。如果他返回一个原始值,JavaScript 将这个值转换为字符串,并返回这个字符串结果。
- 如果对象没有 toString 方法,或者这个方法并不返回一个原始值,那么 JavaScript 会调用 valueOf 方法。如果存在这个方法,则 JavaScript 调用它。如果返回值是原始值,JavaScript 将这个值转换为字符串,并返回这个字符串的结果。
- 否则,JavaScript 无法从 toString 或者 valueOf 获得一个原始值,这时它将抛出一个类型错误异常。
- 对象转数字
其实,从对象到数字的转换也是一样:
|参数类型|结果|
|:–:|:–|
|Object|1.primValue = ToPrimitive(input, Number)
2.返回ToNumber(primValue)|
虽然转换成基本值都会使用 ToPrimitive 方法,但传参有不同,最后的处理也有不同 -> 转字符串调用的是 ToString,转数字调用 ToNumber。
对象转数字的过程中,JavaScript 做了同样的事情,只是它会首先尝试 valueOf 方法。
- 如果对象具有 valueOf 方法,且返回一个原始值,则 JavaScript 将这个原始值转换为数字并返回这个数字。
- 否则,如果对象具有 toString 方法,且返回一个原始值,则 JavaScript 将其转换并返回。
- 否则,JavaScript 抛出一个类型错误异常。
注意,在以上代码块中,[] 和 [0] 都返回了 0,而 [1, 2, 3] 却返回了一个 NaN。我们分析一下原因:// 例子 console.log(Number({})) // NaN console.log(Number({a : 1})) // NaN console.log(Number([])) // 0 console.log(Number([0])) // 0 console.log(Number([1, 2, 3])) // NaN console.log(Number(function(){var a = 1;})) // NaN console.log(Number(/\d+/g)) // NaN console.log(Number(new Date(2010, 0, 1))) // 1262275200000 console.log(Number(new Error('a'))) // NaN
- 当我们 Number([]) 的时候,先调用 [] 的 valueOf 方法,此时返回 [],因为返回了一个对象而不是原始值,所以又调用了 toString 方法,此时返回一个空字符串,接下来调用 ToNumber 这个规范上的方法,参照对应表,转换为 0, 所以最后的结果为 0。
- 而当我们 Number([1, 2, 3]) 的时候,先调用 [1, 2, 3] 的 valueOf 方法,此时返回 [1, 2, 3],再调用 toString 方法,此时返回 1,2,3,接下来调用 ToNumber,参照对应表,因为无法转换为数字,所以最后的结果为 NaN。
隐式数据类型转化
一元操作符 +
当 + 运算符作为一元操作符的时候,会调用 ToNumber 处理该值,既然是调用 ToNumber 方法,当输入的值是对象的时候,先调用 ToPrimitive(input, Number) 方法,例子:
console.log(+[]);
// [] 调用 valueOf 方法,返回一个空数组;
// 因为不是原始值,继续调用 toString 方法,返回 "";
// 得到返回值后,然后再调用 ToNumber 方法,"" 对应的返回值是 0,所以最终返回 0。
console.log(+['1']); // 1
console.log(+['1', '2', '3']); // NaN
console.log(+{}); // NaN
二元操作符 +
当计算 value1 + value2时:
lprim = ToPrimitive(value1)
rprim = ToPrimitive(value2)
- 如果 lprim 是字符串或者 rprim 是字符串,那么返回 ToString(lprim) 和 ToString(rprim)的拼接结果
- 返回 ToNumber(lprim) 和 ToNumber(rprim)的运算结果
1.Null 与数字
console.log(null + 1);
- lprim = ToPrimitive(null) 因为null是基本类型,直接返回,所以 lprim = null
- rprim = ToPrimitive(1) 因为 1 是基本类型,直接返回,所以 rprim = 1
- lprim 和 rprim 都不是字符串
- 返回 ToNumber(null) 和 ToNumber(1) 的运算结果:ToNumber(null) 的结果为0,ToNumber(1) 的结果为 1。所以,null + 1 相当于 0 + 1,最终的结果为数字 1。
2.数组与数组
console.log([] + []);
- lprim = ToPrimitive([]),[]是数组,相当于ToPrimitive([], Number),先调用valueOf方法,返回对象本身,因为不是原始值,调用toString方法,返回空字符串””
- rprim类似。
- lprim和rprim都是字符串,执行拼接操作:[] + []相当于 “” + “”,最终的结果是空字符串””。
3.数组与对象
// 两者结果一致
console.log([] + {});
console.log({} + []);
- lprim = ToPrimitive([]),lprim = “”
- rprim = ToPrimitive({}),相当于调用 ToPrimitive({}, Number),先调用 valueOf 方法,返回对象本身,因为不是原始值,调用 toString 方法,返回 “[object Object]”
- lprim 和 rprim 都是字符串,执行拼接操作:[] + {} 相当于 “” + “[object Object]”,最终的结果是 “[object Object]”。
console.log(1 + true); // 2 console.log({} + {}); // "[object Object][object Object]" console.log(new Date(2017, 04, 21) + 1) // "Sun May 21 2017 00:00:00 GMT+0800 (CST)1"
== 相等
null和undefined
console.log(null == undefined); 规则2)x是null并且y是undefined,返回true 规则3)x是undefined并且y是null,返回true // 所以例子的结果自然为 true。
字符串与数字
console.log('1' == 1); 规则4)x是数字,y是字符串,判断x == ToNumber(y) 规则5)x是字符串,y是数字,判断ToNumber(x) == y // 都先转换成数字后再进行比较,所以例子的结果为true。
布尔值和其他类型
console.log(true == '2') 规则6)x是布尔值,判断ToNumber(x) == y 规则7)y是布尔值,判断x ==ToNumber(y) // 当一方出现布尔值的时候,就会对这一方的值进行ToNumber处理,也就是说true会被转化成1,true == '2' 就相当于 1 == '2' 就相当于 1 == 2,结果自然是 false。 // 所以当一方是布尔值的时候,会对布尔值进行转换,因为这种特性,所以尽量少使用 xx == true 和 xx == false 的写法。
对象与非对象
console.log( 42 == ['42']) 规则8)x是字符串或者数字,y是对象,判断x == ToPrimitive(y) 规则9)x是对象,y不是字符串或者数字,判断ToPrimitive(x) == y // 以这个例子为例,会使用 ToPrimitive 处理 ['42'],调用valueOf,返回对象本身,再调用 toString,返回 '42',所以42 == ['42'] 相当于 42 == '42' 相当于42 == 42,结果为 true。
其他:一概返回 false
再多举几个例子进行分析:console.log(false == undefined) // false == undefined 相当于 0 == undefined 不符合上面的情形,执行最后一步 返回 false console.log(false == []) // false == [] 相当于 0 == [] 相当于 0 == '' 相当于 0 == 0,结果返回 true console.log([] == ![]) // 首先会执行 ![] 操作,转换成 false,相当于 [] == false 相当于 [] == 0 相当于 '' == 0 相当于 0 == 0,结果返回 true
最后再举一些会让人踩坑的例子:
console.log(false == "0")
console.log(false == 0)
console.log(false == "")
console.log("" == 0)
console.log("" == [])
console.log([] == 0)
console.log("" == [null])
console.log(0 == "\n")
console.log([] == 0)
// 以上均返回 true
常见的类型转换
类型 | 值 | to Boolean | to Number | to String |
---|---|---|---|---|
Boolean | true | true | 1 | “true” |
Boolean | false | false | 0 | “false” |
Number | 123 | true | 123 | “123” |
Number | Infinity | true | Infinity | “Infinity” |
Number | 0 | false | 0 | “0” |
Number | NaN | false | NaN | “NaN” |
String | “” | false | 0 | “” |
String | “123” | true | 123 | “123” |
String | “123abc” | true | NaN | “123abc” |
String | “abc” | true | NaN | “abc” |
Null | null | false | 0 | “null” |
Undefined | undefined | false | NaN | “undefined” |
Function | function() | {} | true | NaN |
Object | {} | true | NaN | “[object Object]” |
Array | [] | true | 0 | “” |
Array | [“abc”] | true | NaN | “abc” |
Array | [“123”] | true | 123 | “123” |
Array | [“123”, “abc”] | true | NaN | “123, abc” |