JavaScript运算符规则
从sb说起
console.log((!(~+[])+{})[--[~+""][+[]]*[~+[]]+~~!+[]]+({}+[])[[~!+[]]*~+[]])
控制台输出此段字符,你会得到"sb"。
有没有那么点把丝头疼…让我们顶着头疼分析一下
运算符在这里(!(~+[])+{})[--[~+""][+[]]*[~+[]]+~~!+[]]+({}+[])[[~!+[]]*~+[]]
,从最开始出发,看能不能弄简单一丢丢
小括号里边的可以先算,没问题的吧?第一个小括号(!(~+[])+{})
,先算里边的那个小括号,就是~+[]
。+[]
默认把空数组转成数字,结果是数字0,~0
结果是-1(~的运算规则)。
所以就是(!(-1)+{})
,即(false+{})
,加号两边没法加的时候会把两边都转换成字符串变成字符串的连接操作(自动数据类型转换),所以结果是("false[object Object]")
。至此第一个小括号完成了。
中括号在js中要么是数组,比如[1,2,3]
这一类,要么是用来取出对象的成员,比如obj["name"]
这种。这里小括号和中括号之间没有任何运算符相连,我们可以知道后边肯定是取出对象的成员。把这个中括号单独拿出来是这一段[--[~+""][+[]]*[~+[]]+~~!+[]]
。
还是来慢慢分析。
[~+""]
,其中+""
即把""
转换成数字,也就是数字0(自动数据类型转换),~0
的值为-1,所以是[-1]
[+[]]===[0]
[~+[]]===[~0]===[-1]
~~!+[]===~~!0===~~true===~(-2)===1
,这一段比较关键的还是自动数据类型转换和~的运算规则
所以由上边的分析可以得到[--[~+""][+[]]*[~+[]]+~~!+[]] === --[-1][0]*[-1]+1
,此时[-1]
作为一个数组,[0]
用来取出数组的第0个元素,所以是--(-1)*[-1]+1
。
--(-1)===-2
,-2*[-1]
会把后者转换成数字-1
,所以结果是数字2,2+1
结果是3。
第一阶段得到的结果是("false[object Object]")[3]
,取字符串的第四个字符,也就是s
继续算后面的({}+[])[[~!+[]]*~+[]]
。
({}+[])===("[object Object]0")
后边中括号里的先算前面的一半[~!+[]]===[~!0]===[~true]===[-2]
乘号后边的~+[]===~0===-1
所以是[-2]*(-1)
,即(-2)*(-1)
,结果是数字2。
所以("[object Object]0")[2]
,取字符串的第三个字符,也就是b
至此这个诡异的sb
终于结束。
讲的不是太直观,可以配合下边拆分图食用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hsbjgnRR-1578381325437)(https://i.loli.net/2019/10/15/VHFgZP5K1Ctrlpe.png)]
这里用到比较重要的是运算符导致的数据类型转换,~的运算规则,[]的使用,以及贯穿全局的运算符的优先级。
使用这个思路我们可以反推出其它的字符串,比如说Null
。需要注意的是你所得到的操作数的数据类型。比如NaN的数据类型实际上是number,"NaN"才是string。
(+{}+{})[-[]] === (NaN+{})[-[]] === ("NaN[object Object]")[-[]] === ("NaN[object Object]")[0] === "N"
(!![]+[])[-(~[]+~[])] === (!false+0)[-(~[]+~[])] === ("true0")[-(~[]+~[])] === ("true0")[-( (-1) + (-1) )] === ("true0")[-(-2)] === ("true0")[2] === "u"
(![]+[])[-(~[]+~[])] === (false+0)[2] === ("false0")[2] === "l"
(![]+[])[-(~[]+~[])] === (false+0)[2] === ("false0")[2] === "l"
console.log((+{}+{})[-[]]+(!![]+[])[-(~[]+~[])]+(![]+[])[-(~[]+~[])]+(![]+[])[-(~[]+~[])]) //Null
标点符号导致的自动数据类型转换
加减乘除等基础运算中,除了加号,所有的基础运算符都会将运算符两边的运算子自动转换为Number类型。但是加号的话,只要两个运算子不都是Number类型,都会转成String类型。所以我们可以看到下面这奇怪的结果。
1+[1] === "11" // string
1-[1] === 0 // number