你可能在網上見過有人用 幾個不一樣的字符寫的各類稀奇古怪的 JavaScript 代碼,雖然看起來奇怪,可是能正常運行!好比這個:javascript
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
複製代碼
你猜運行結果是什麼?你能夠本身去控制檯試一下。前端
看起來很神奇,但這究竟是怎麼回事呢?java
事實上,你幾乎能夠用下面這 6 個字符寫出任意的 JavaScript 程序:數組
[]()!+
複製代碼
不少人都知道這個技巧,可是沒有多少開發人員知道它究竟是如何工做的。今天,咱們就來看看它背後的執行原理。咱們的目標是用這幾個字符來寫出字符串「self」
。姑且用這個字符串向 Self 語言致敬,JavaScript 的靈感來源之一就是它。bash
咱們之因此可以拋開其餘字符不用,要歸功於 JavaScript 的類型系統和數據類型轉換機制。微信
這 6 個字符是這樣各顯神通的:[]
能夠用來建立數組,!
和+
能夠在數組上執行一些操做,再用()
給這些操做分組。spa
先看一個簡單的數組:翻譯
[]
複製代碼
數組前加上!
會把它轉成布爾值。數組被認爲是真值,所以取非以後變成了false
:code
![] === false
複製代碼
除非轉換爲相似類型,不然沒法將不一樣類型的值加在一塊兒。JavaScript 在進行轉換時遵循一個預約義的規則:cdn
在表達式2 + true
中,JavaScript 會將true
轉成數字,獲得表達式2+1
。
在表達式2 + "2"
中,JavaScript 會將數字轉成字符串,獲得2 + "2" === "22"
。
這些轉換規則還不算糟糕,可是對於其餘類型,好戲立刻來了。
數組相加會轉換成字符串並鏈接起來。空數組轉換爲空字符串,所以將兩個數組相加將獲得空字符串。
[] + [] === "" + "" === ""
複製代碼
數組跟其餘類型值相加時也同樣:
![] + [] === "false" + "" === "false"
複製代碼
驚不驚喜?咱們獲得了目標字符串"self"
所包含的幾個字符!
若是咱們能產生一些數字,就能夠按正確的順序提取所需的字符:
"false"[3] === "s"
(![] + [])[3] === "s"
複製代碼
那麼,如何生成數字呢?
前面提到了,能夠把數組轉成布爾值。那若是用加號+
把它轉成數字會怎樣?
+[] === ???
複製代碼
JavaScript 會嘗試調用數組的valueOf
方法,可是發現不存在這個方法,而後就轉而調用toString()
方法了。所以上面的代碼等效於:
+[] === +""
複製代碼
將字符串轉換爲數字將產生如下結果:
+"42" === 42
+"esg" == NaN
+"" === 0
複製代碼
空字符串是一個 false
值,跟 null,undefined和數字零相似,所以將其中任何一個轉換爲數字都會變成零:
+null === 0
+undefined === 0
+false === 0
+NaN === 0
+"" === 0
複製代碼
所以,將數組轉換爲數字須要先將其轉換爲字符串,最後轉成 0:
+[] === +"" === 0
複製代碼
第一個數字已經造出來了!咱們還須要更多數字,繼續:
!0 === !false
!false === true
!0 === true
複製代碼
將 0
取否就獲得一個爲真的布爾值。爲真的布爾值轉成數字,就是1
:
+true === 1
複製代碼
有了 1
,天然就能夠獲得2
,所謂道生一,一輩子二,二生三,三生萬物……
用上面的轉換大法,能夠輕鬆獲得咱們想要的這些數字:
1 === +true == +(!0) ==== +(!(+[])) === +!+[]
1 === +!+[]
2 === +!+[] +!+[]
3 === +!+[] +!+[] +!+[]
4 === +!+[] +!+[] +!+[] +!+[]
複製代碼
總結下這些規則:
false
: ![] // false
[] + [] // ""
0
,再去否獲得 true
,再轉成數字獲得1
:+(!(+[])) === 1
根據這些規則,咱們就能獲得想要的字符串。看下面這個示意圖就很清楚了:
![] + [] === "false"
+!+[] === 1
(![] + [])[3] + (![] + [])[4] + (![] + [])[2] + (![] + [])[0]
^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^
"false" "false" "false" "false"
^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
s e l f
複製代碼
最終的表達式就是這樣:
(![] + [])[+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]] +
(![] + [])[+[]]
複製代碼
整理下空格和換行,就是一行代碼:
(![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+[]]
複製代碼
如今你應該明白了那些神奇 JavaScript 代碼的原理了吧?發揮你的想象,看還能寫出其餘什麼來?好比,今天是聖誕節,來個 「Merry Christmas」?
Anyway,Merry Christmas!
本文根據https://javascript.christmas/2019/17翻譯整理,加了一些本身的內容
更多前端技術乾貨盡在微信公衆號:1024譯站