js 隐式转换
一、ToPrimitive内部运算
1.定义
ToPrimitive运算,它会用于对象转换为原始数据类型,这个运算不只会用在加号运算符,也会用在关系比较或值相等比较的运算中。
语法:ToPrimitive(input, PreferredType?)
input代表代入的值,而PreferredType可以是数字(Number)或字符串(String)其中一种,没有PreferredType默认为数字(Number)。如果input为原始值,则返回原始值;如果为对象,则转换如下:
2.PreferredType为数字(Number)
1).调用对象的valueOf()方法,如果能得到原始数据类型的值,则返回这个值。
2).调用对象的toString()方法,如果能得到原始数据类型的值,则返回这个值。
3).否则,抛出TypeError错误。
3.PreferredType为数字(String)
1).调用对象的toString()方法,如果能得到原始数据类型的值,则返回这个值。
2).调用对象的valueOf()方法,如果能得到原始数据类型的值,则返回这个值。
3).否则,抛出TypeError错误。
4.实例
1)字符串 + 其他原始类型
字符串在加号运算有最高的优先运算,与字符串相加必定是字符串连接运算。所有的其他原始数据类型转为字符串
2)数字 + 其他的非字符串的原始数据类型
数字与其他类型作相加时,除了字符串会优先使用字符串连接运算的,其他都要依照数字为优先,所以除了字符串之外的其他原始数据类型,都要转换为数字来进行数学的相加运算。
3)数字/字符串以外的原始数据类型作加法运算
当数字与字符串以外的,其他原始数据类型直接使用加号运算时,就是转为数字再运算,这与字符串完全无关。
4)[] + []
数组会调用valueOf和toString方法,[] + [] => '"" + "" = ""得到空字符串
5){} + {}
在谷歌浏览器上,{}会调用 valueOf和toString方法得到“[object Object]”,两个相加便得到"[object Object][object Object]"
在Firefox、Edge浏览器则不同,会把前面的{}当做区块语句,略过,剩下+{},进行Number({})运算,相当于Number("[object Object]")运算,最后得出的是NaN
6){} + []
浏览器把{}当做区块语句略过,剩下+[],相当于进行Number([])运算,Number("") => 0
7)[] + {}
相当于"" + "[object Object]" => "[object Object]"
8)Date对象
Date对象调用valueOf和toString方法有些不同(一般对象会先调用valueOf再调用toString),Date对象会先调用toString再调用valueOf
二、++[[]][+[]]+[+[]] == 10
首先将++[[]][+[]]+[+[]] 分解为++[[]][+[]] 和 [+[]]
1.[+[]]
+[],[]是对象,进行toPrimitive运算,调用valueOf方法得到[]不是原始值,然后调用toString方法得到空字符串,转换为数字后得到0,那么[+[]]等于[0]
2.++[[]][+[]]
将++[[]][+[]]变为
var val = [[]][+[]];
++val;
根据上面得出,[+[]]为0,[[]][+[]] => [[]][0] => []
进一步拆分
var val = [];
++val;
因为++时候旧的值要进行 ToNumber() 运算(ToNumber把其他类型按照一定的规则转化成数字类型,也就是类似Number())
所以++val 相当于 val = ToNumber([]) + 1 => 0 + 1 => 1
3.++[[]][+[]]+[+[]]
++[[]][+[]]+[+[]] => 1 + [0] => 1 + "0" => "10"
所以++[[]][+[]]+[+[]] == 10
三、用隐式转换输出"I love you"
(+!![]/+[]+[])[+[]]+([]+{})[~!![]*~!![]*-~!![]-!![]]+(![]+[])[-~!![]]+([]+{})[+!![]]+([][(![]+[])[!![]-~!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]]+[])[[]+(+!![]+!![])+(!![]-~!![])]+(!![]+[])[!![]-~!![]]+([]+{})[~!![]*~!![]*-~!![]-!![]]+(+!![]/+[]+[])[-~-~-~-~-~-~!!{}]+([]+{})[+!![]]+(!![]+[])[-~!![]]
转换步骤:
I:(+!![]/+[]+[])[+[]] // 1/0得到 Infinity,加上[]得到"Infinity","Infinity"[0] => "I"
" ": ([]+{})[~!![]*~!![]*-~!![]-!![]] // [] + {} => "[object Object]", "[object Object]"[7] => " "
l:(![]+[])[-~!![]] // "false"[2] => "l"
o: ([]+{})[+!![]] // "[object Object]", "[object Object]"[1] => "o"
v:([][(![]+[])[!![]-~!![]]+([]+{})[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]]+[])[[]+(+!![]+!![])+(!![]-~!![])]
1) []['sort']+[] => "function sort() { [native code] }"
2) "function sort() { [native code] }"[23] => "v"
e:(!![]+[])[!![]-~!![]] // "true"[3] => "e"
" " : ([]+{})[~!![]*~!![]*-~!![]-!![]] // [] + {} => "[object Object]", "[object Object]"[7] => " "
y:(+!![]/+[]+[])[-~-~-~-~-~-~!!{}] // "Infinity"[7] => "y"
o:([]+{})[+!![]] // "[object Object]", "[object Object]"[1] => "o"
u:(!![]+[])[-~!![]] // "true"[2] => "u"
四、== 进行对比的转换
1. 对象和布尔值,对象先转换为字符串,然后再转换为数字,布尔值直接转换为数字
[] == true; // false []转换为字符串'',然后转换为数字0,true转换为数字1,所以为false
2. 对象和字符串,对象转换为字符串,然后两者进行比较。
[1,2,3] == '1,2,3' // true [1,2,3]转化为'1,2,3',然后和'1,2,3', 结果为true;
3. 对象和数字,对象先转换为字符串,然后转换为数字,再和数字进行比较。
[1] == 1; // true `对象先转换为字符串再转换为数字,二者再比较 [1] => '1' => 1 所以结果为true
4. 字符串和数字,字符串转换成数字,二者再比较。
'1' == 1 // true
5. 字符串和布尔值,二者全部转换成数值再比较。
'1' == true; // true
6. 布尔值和数字,布尔转换为数字,二者比较。
true == 1 // true
7.undefined == null //true undefined和null 比较返回true,二者和其他值比较返回false
五、运算符的优先级
参考:
1.https://segmentfault.com/a/1190000008038678
2.https://segmentfault.com/a/1190000008572281
3.https://www.cnblogs.com/chenmeng0818/p/5954215.html
4.https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence