前言
(!(~+[])+{})[--[~+""][+[]]*[~+[]]+~~!+[]]+({}+[])[[~!+[]]*~+[]]
大家可以试着运行一下这串代码
是不是输出的"sb"让人眼前一惊
肯定都会去想这个变相的骂人是怎么实现的
原始式子拆分
解这种题目,当然要把他们先按运算符给拆分开来,一步一步分析了
首先可以将原始的式子分为
- 式子1 (!(~+[])+{})[–[~+""][+[]]*[~+[]]+~~!+[]]
- 式子2 ({}+[])[[~!+[]]*~+[]]
并且式子1和式子2通过 “+” 运算符连接
拆分式子1 (!(~+[])+{})[–[~+""][+[]]*[~+[]]+~~!+[]]
式子1又可以分为
- 式子3: (!(~+[])+{})
- 式子4: --[[~+""][+[]]]*[~+[]]+~~!+[]
并且式子3和式子4通过 “[]” 连接
式子3又可以分为
- 式子5: (!(~+[]))
- 式子6: {}
并且式子5和式子6用 “+” 运算符连接
式子5 (!(~+[])) 结果
先来看式子5 (!(~+[]))
优先级为先执行+,然后执行~
所以+[] 一元+,触发toNumber,而[]的toNumber为0
所以现在变成了!(~0),而~是一元位运算 触发toInt32 所以 ~0为 -1
所以现在式子5成为了!(-1), 此时-1执行toBoolean为true
所以式子5为false
式子3 (!(~+[])+{}) 结果
式子3是式子5和式子6通过+链接 所以就是 false + {}
这是二元+,所以会对左右两边的值进行hint为空的也就是number时的toPrimitive,先执行valueOf,false的valueOf为false,为基本类型值直接返回,而{}经过valueOf后返回{},不为基本类型值,所以继续执行toString,{}.toString为"[object Object]"
执行完toPrimitive后,判断两边值是否有string类型,而这里的"[object Object]"是string类型,所以执行拼接操作
所以式子3为 “false[object Object]”
拆分式子4 --[[~+""][+[]]]*[~+[]]+~~!+[]
式子4可以分为
- 式子7 --[[~+""][+[]]]*[~+[]]
- 式子8 ~~!+[]
并且式子7和式子8通过 “+” 运算符连接
拆分式子7 --[[~+""][+[]]]*[~+[]]
式子7又可分为
- 式子9 --[[~+""][+[]]]
- 式子10 [~+[]]
并且式子9和式子10通过 “*” 运算符连接
拆分式子9 --[[~+""][+[]]]
式子9又可分为操作符–与
- 式子11 [~+""]
- 式子12 +[]
并且式子11和式子12通过 “[]” 连接
式子11[~+""] 结果
接下来分析式子11 [~+""]
同样先执行+,一元+会触发toNumber,"".toNumber为0,
所以此时为0,前面也说了0为-1
因此式子11为[-1]
式子12 +[] 结果
式子12 +[]
触发toNumber [].toNumber为 0
因此式子12为0
式子9 --[[~+""][+[]]] 结果
而式子11和式子12通过[]连接,又有–操作符在前
所以式子9为–[-1][0],这里[-1][0]取得就是-1这个数了,数组的取值而已,没多大问题,然后执行–
此时式子9为 -2
式子10 [~+[]] 结果
然后分析式子10 [~+[]]
同理,先+[] ,触发toNumber,为0 ,
[~0]为[-1],
所以式子10为[-1]
式子7 --[[~+""][+[]]]*[~+[]] 结果
式子7是式子9和式子10通过*连接的
所以式子7为 -2 * [-1]
而二元*运算符,会将左右两边值都进行toNumber,-2的toNumber为-2,[-1]的toNumber,会触发toPrimitive
先执行valueOf,返回的数组本身,不是基本类型值,因此执行toString,所以为"-1",为"-1"继续执行toNumber,所以为-1,
因此式子7为-2 * -1,式子7为2
式子8 ~~!+[] 结果
此时分析式子8 ~~!+[]
先执行+ ,触发toNumber,为0。
然后执行!0,0执行toBoolean,0变成false,所以!false为true
然后执行~true。 true执行toInt32,为1,所以~1为-2 ,
然后执行~-2,为1
所以式子8为1
式子4 --[[~+""][+[]]]*[~+[]]+~~!+[] 结果
而式子4是式子7和式子8通过+连接的
所以式子4为 2 + 1,式子4为3
式子1 结果
式子1是式子3和式子4通过[]连接的
所以式子1 为 “false[object Object]”[3]
所以式子1为下标为3的元素,也就是"s"
拆分式子2 ({}+[])[[~!+[]]*~+[]]
现在分析式子2 ({}+[])[[~!+[]]*~+[]]
式子2可分为
- 式子13 ({} + [])
- 式子14 [~!+[]]*~+[]
并且式子13和式子14通过 “[]” 连接
拆分式子14 [~!+[]]*~+[]
式子14可分为
- 式子15 [~!+[]]
- 式子16 ~+[] ,
并且式子15和式子16通过 “*” 运算符连接
式子13 ({} + []) 结果
现在分析式子13 ({} + [])
这是很经典的,前面也分析过,双元+,会对左右两边的值进行toPrimitive
hint值为空,所以执行hint为Number的[[DefaultValue]],所以先执行valueOf,而{} 和[]的valueOf都是返回本身,也都不是基本类型值
因此会继续执行toString,而{} 的toString为"[object Object]",而[]的toString为"",因此再判断左右两边值是否有string类型,两边都是string类型,所以进行拼接操作
所以 “[object Object]” + “”
所以式子13为"[object Object]"
式子15 [~!+[]] 结果
现在分析式子15 [~!+[]]
同理,先执行+[],触发toNumber,[]的toNumber为0
执行!0,0先toBoolean为false,然后!false为true
然后~true,true先toInt32为1
然后~1为-2
所以式子15为[-2]
式子16 ~+[] 结果
现在分析式子16 ~+[]
同理 先执行+[],为0,
然后~0
所以式子16为-1
式子14 [~!+[]]*~+[] 结果
式子14是式子15和式子16通过*连接
所以式子14为 [-2] * -1 ,而双元* ,会对左右两边的进行toNumber
[-2]的toNumber首先会触发toPrimtive,首先valueOf返回[-2],不是基本类型值,继续执行toString,为"-2","-2"再执行toNumber,为-2
而-1的toNumber当然就是-1
所以式子14为-2 * -1,所以式子14为2
式子2 结果
式子2是式子13和式子14通过[]连接
因此式子4为"[object Object]"[2]
因此式子2也就是索引为2的元素:“b”
最初的式子的结果
最初的式子是式子1和式子2通过+连接的
所以最初的式子为 “s” + “b”,双元+运算符
对两边值进行toPrimitive,首先执行valueOf,返回本身,且为基本类型值,所以返回"s"与"b"
执行完toPrimitive后,判断左右两边值是否有string类型,两边都为string类型,所以执行拼接操作
所以"s" + “b”,最初的式子为"sb",也是挺有趣的一句话鸭
总结
这算是一道考验对类型转换规范的题目,而且考的比较详细了,如果能够正确分析出来,可以说是类型转换掌握的差不多了!