最近刚开始复习JS的基础知识,看到隐式转换这一块发现它的规则很多,红宝书上列出的框框又有些冗杂,所以这里我根据自己的理解总结一下其中主要的隐式转换规律。
1、== 操作符
1)若存在Boolean类型 :比较相等性之前先将其转换为数值,true == 1、false == 0
2)若存在一String类型另一数值型:比较之前先将其转换为数值,调用Number(str)
3)若存在一Object类型另一其它:调用对象的valueOf()方法,得到基本类型后再按照前面的规则进行比较。
2、Number() 可将任意类型转换为数值型
1)true==1、false == 0、“” == 0(空字符串)
2)识别多种进制,格式正确返回数字,格式错误返回NaN
十六进制0XA转换为十进制数字10,而后者格式无法识别返回NaN。
3)操作数为对象时,先执行对象的valueOf(),若转换结果为NaN则调用对象的toString()方法。
数组的valueOf()方法返回数组自身,toString()返回空字符串转换为数值0。
对象的valueOf()方法返回对象本身,toString()返回一个对象类型名称字符串无法转换为数值返回NaN。
3、位运算符 非数值型Number()隐式转换后运算
1) ~ 按位非(操作数的相反数-1)
2) & 按位与(相同为1不同为0)
3) | 按位或(有一个为1就是1)
4) ^ 按位异或(相同为0不同为1)
5) << 左移(不影响符号位)n<
6) >> 有符号右移(保留符号位)n>>m==n/2m
7) >>> 无符号右移(会将负数的二进制码(补码)当作正数的二进制码运算)
4、逻辑运算符
1)!逻辑非 仅返回布尔值(!! 相当于 Boolean(),将任意类型转换为布尔型)
2) || 逻辑或:一真则真两假为假
第一个操作数为假时返回第二个操作数;
第一个操作数为真时返回第一个操作数。
3) && 逻辑与:一假则假两真为真
第一个操作数为假时返回第一个操作数;
第一个操作数为真时返回第二个操作数。
注:上述规则适用于任何操作数类型,为真(true、非空str、非零数值、任何对象),为假(false、空str、0、NaN、Null、undefined)
5、++/--自增自减操作符
先对非数值操作数进行隐式Number()转换,后执行加一或减一操作。
6、加减以及乘性运算符
1)+、- 实现字符串和数值的相互转换
字符串转换为数值:+"10" === 10 (非数值+/-操作时应用Number()转换规则)
数值转换为字符串:10+"" === "10" (此处加号作为字符串拼接符,对数值调用toString()方法)
2)*、/、%
同样对非数值调用Number()后进行运算操作。
注:% 运算符运算结果的符号取决于第一个操作数。
7、优先级问题(图偷来的)
8、隐式转换应用
相信不少人都见过上面这一式子,乱七八糟的符号堆在一起,像我这样的初学者一看根本不知道是什么,更想不到它还能返回结果。
接下来我就结合之前列出的一些规律简单解析一下这个式子,来看看其中的隐式转换是如何起作用并返回正确结果的。
1)整体分析抽出骨架
2)Part A : ( ! ( ~ + [ ] ) + { } )
!(~ + [ ]) 加号在这里起正号作用,+[ ]经过Number()隐式转换返回0,而对0取反返回-1,最后逻辑非返回false。
于是式子简化为( false + { } ),加法运算符调用Number(),上面已经说过{ }最后会调用toString()方法,进而式子变成了( false+"[object Object]" ),加法运算符碰到字符串又变成了字符串拼接符。
3)Part B : [ --[ ~ + "" ][ +[ ] ] * [ ~ + [ ] ] + ~~! +[ ] ]
先按将式子按隐式转换规律简化(依旧是Number())
[ --[ ~ + "" ][ +[ ] ] * [ ~ + [ ] ] + ~~! +[ ] ]; [ --[ ~ + 0 ][ 0 ] * [ -1 ] + ~~!0 ]; [ --[ -1 ][ 0 ] * [ -1 ] + ~~true ]; [ --( -1 ) * [ -1 ] + 1 ]; //两次取反返回自身 [ -2 * [ -1 ] + 1 ]; //自增减运算符优先级高于*运算符 [ 2 + 1 ];
4)Part A + B = what?
("flase[object Object]")[3],这不就是按索引访问字符串中的值嘛,返回结果"s";
下面也是同样套路。
5)Part C
两个对象最后各自调用了toString()方法,得到字符串。
6)Part D
[ [ ~! + [ ] * ~ +[ ] ] ]; [ [ ~true * -1 ] ]; [ [ -2 * -1 ] ]; [ [ 2 ] ];
这里包裹的两层中括号,实际上也是应用了隐式转换,内层的数组 [2] 调用toString()最终返回数值 2;
7)最后,"s"+"b"拼接起来就出现了前面的返回结果。
总结:
坦白讲,像上面那样的式子除了出现在面试题中其他地方肯定是见不到的,但这也给了我们一些启示。他的隐式转换是后台自动调用的,这就增加了一些不可控性,另外自动的隐式转换还会引起额外的性能消耗。应该尽量避免隐式转换的使用让程序更稳定。