无论是在技术聊天群仍是论坛里,总能碰到 x + y 等于多少的问题,好比 [] + {} == ?
,若是你不了解其中的原理,那么就插不上话,只能眼睁睁地等大佬解答了。javascript
说到底仍是JS类型转换的问题,首先咱们先温习一下JS的7种内置类型:java
是否是感受还有Function,毕竟能用typeof获取到?不,函数、数组都是Object的子类型。数组
类型分为基本类型
和复合类型
两种,除了对象,其它都是基本类型。函数
发音:[ˈprɪmətɪv]
结构:toPrimitive(input, preferedType = number)
在对象的隐式转换中,对象须要先转成基本类型,并按照以下顺序执行。学习
valueOf()
。toString()
方法。TypeError
异常。Uncaught TypeError: Cannot convert object to primitive value
接着,咱们看下各个对象的转换实现code
对象 | valueOf() | toString() |
---|---|---|
Object | 原值 | 字符串 => '[object Object]' |
Function | 原值 | 字符串 => 'function xyz() {...}' |
Array | 原值 | 字符串 => 'x,y,z' |
Date | 数字时间戳 | 字符串 => "Sat May 22 2021..." |
Date的默认preferedType=string,即在加法运算中先执行toString()。在 - | * | / | +x | -x 等运算中,先执行valueOf()对象
数组的toString()能够等效为
join(',')
其中,数组toString()时,遇到null, undefined都被忽略,遇到symbol直接报错,遇到没有toString()的对象也报错。ip
[1, null, undefined, 2].toString() === '1,,,2'; // Uncaught TypeError: Cannot convert a Symbol value to a string [1, Symbol('x')].toString() // Uncaught TypeError: Cannot convert object to primitive value [1, Object.create(null)].toString()
一些特殊值转为数字的例子,等下要用到字符串
Number("0") === 0; Number("") === 0; Number(" ") === 0; Number("\n") === 0; Number("\t") === 0; Number(null) === 0; Number(false) === 0; Number(true) === 1; Number(undefined); // NaN Number("x"); // NaN
加减法运算中遵循了一些隐式转换规则:input
({}).toString() === "[object Object]" 1 + {} === "1[object Object]" [2, 3].toString() === "2,3" 1 + [2, 3] === "12,3" [1] + [2, 3] === "1,2,3" function test() {} test.toString() === "function test() {}" 10 + test === "10function test() {}"
字符串拼接
上面的对象最后也都转成了字符串,遵循本条规则。接着来几个纯字符串的例子
1 + "1" === "11" 1 + 1 === 2 "1" + 1 === "11" "1" + "1" === "11"
3 - 1 === 2 3 - '1' === 2 '3' - 1 === 2 // [].toString() => "" => Number(...) => 0 3 - [] === 3 // {}.toString() => "[object Object]" => Number(...) => NaN 3 - {} // NaN
1 + true === 2 1 + false === 1 1 + null === 1 1 + undefined // NaN
+ 0 === 0 - 0 === -0 1 + + "1" === 2 1 + + + + ["1"] === 2 // 负负得正 1 + - + - [1] === 2 // 负负得正 1 - + - + 1 === 2 1 - + - + - 1 === 0 1 + + [""] === 1 // ["1", "2"].toString() => "1,2" => Number(...) => NaN 1 + + ["1", "2"] // NaN // 吃根香蕉🍌 ("ba" + + undefined + "a").toLowerCase() === "banana"
回到一开始抛出的问题[] + {}
,这样太简单了吧?
[].toString() === ""; {}.toString() === "[object Object]"; [] + {} === "[object Object]";
不是对象是什么?是你的八块腹肌?别急,看看经典的例子
{} + [] === 0; { a: 2 } + [] === 0;
这啥玩意?说好的"[object Object]"呢?
好吧,这是{}
其实表明的是代码块,最后就变成了+ []
,根据前面的原则,数组先被转换成字符串""
,接着由于+x的运算,字符串被转成数字0
。
那 { a: 2 } 总该是对象了吧?其实这时候a
不是表明对象属性,而是被当成了标签(label),标签这东西IE6就已经有了。因此若是咱们写成 { a: 2, b: 3 } + []
这样是会报错的,逗号要改为分号才能经过编译。
若是在表达式中有symbol类型,那么就会直接报错。好比1 + Symbol("x")
报错以下:
Uncaught TypeError: Cannot convert a Symbol value to a number
相等于全等都须要对类型进行判断,当类型不一致时,宽松相等会触发隐式转换。下面介绍规则:
{} != {} [] != {} [] != []
// 当心代码块 "[object Object]" == {} [] == "" [1] == "1" [1,2] == "1,2"
"2" == 2 [] == 0 [1] == 1 // [1,2].toString() => "1,2" => Number(...) => NaN [1,2] != 1
// [] => "" => Number(...) => 0 // false => 0 [] == false // [1] => "1" => 1 // true => 1 [1] == true // [1,2] => "1,2" => NaN // true => 1 [1,2] != true "0" == false "" == false
null、undefined与任何非自身的值对比结果都是false,可是null == undefined
是一个特例。
null == null undefined == undefined null == undefined null != 0 null != false undefined != 0 undefined != false Symbol('x') != Symbol('x')
对比不像相等,能够严格相等(===)防止类型转换,对比必定会存在隐式类型转换。
[] < [] // false [] <= {} // true {} < {} // false {} <= {} // true
// ["06"] => "06" => 6 ["06"] < 2 // false ["06"] < "2" // true ["06"] > 2 // true 5 > null // true -1 < null // true 0 <= null // true 0 <= false // true 0 < false // false // undefined => Number(...) => NaN 5 > undefined // false
欢迎纠正错误,欢迎学习交流收藏。我是原罪,一个极客。