只用這 6 個字符,就可以寫出任意 JavaScript 代碼!


你可能在網上見過有人用 幾個不同的字符寫的各種稀奇古怪的 JavaScript 代碼,雖然看起來奇怪,但是能正常運行!比如這個:

(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]

你猜運行結果是什么?你可以自己去控制台試一下。

看起來很神奇,但這到底是怎么回事呢?

事實上,你幾乎可以用下面這 6 個字符寫出任意的 JavaScript 程序:

[]()!+

很多人都知道這個技巧,但是沒有多少開發人員知道它到底是如何工作的。今天,我們就來看看它背后的執行原理。我們的目標是用這幾個字符來寫出字符串“self”。姑且用這個字符串向 Self 語言致敬,JavaScript 的靈感來源之一就是它。

基本原理

我們之所以能夠拋開其他字符不用,要歸功於 JavaScript 的類型系統和數據類型轉換機制。

這 6 個字符是這樣各顯神通的:[]可以用來創建數組,!+可以在數組上執行一些操作,再用()給這些操作分組。

先看一個簡單的數組:

[]

數組前加上!會把它轉成布爾值。數組被認為是真值,因此取非之后變成了false

![] === false

除非轉換為類似類型,否則無法將不同類型的值加在一起。JavaScript 在進行轉換時遵循一個預定義的規則:

在表達式2 + true中,JavaScript 會將true轉成數字,得到表達式2+1

在表達式2 + "2"中,JavaScript 會將數字轉成字符串,得到2 + "2" === "22"

這些轉換規則還不算糟糕,但是對於其他類型,好戲馬上來了。

JavaScript 數組強制轉換

數組相加會轉換成字符串並連接起來。空數組轉換為空字符串,因此將兩個數組相加將得到空字符串。

[] + [] === "" + "" === ""

數組跟其他類型值相加時也一樣:

![] + [] === "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 代碼的原理了吧?發揮你的想象,看還能寫出其他什么來?比如,2020 年剛到,來個 “Happy New Year”?

Anyway,Happy New Year!

更多前端技術干貨盡在微信公眾號:1024譯站
微信公眾號:1024譯站


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM