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