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

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

(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
複製代碼

你猜運行結果是什麼?你能夠本身去控制檯試一下。前端

看起來很神奇,但這究竟是怎麼回事呢?java

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

[]()!+
複製代碼

不少人都知道這個技巧,可是沒有多少開發人員知道它究竟是如何工做的。今天,咱們就來看看它背後的執行原理。咱們的目標是用這幾個字符來寫出字符串「self」。姑且用這個字符串向 Self 語言致敬,JavaScript 的靈感來源之一就是它。bash

基本原理

咱們之因此可以拋開其餘字符不用,要歸功於 JavaScript 的類型系統和數據類型轉換機制。微信

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

先看一個簡單的數組:翻譯

[]
複製代碼

數組前加上!會把它轉成布爾值。數組被認爲是真值,所以取非以後變成了falsecode

![] === false
複製代碼

除非轉換爲相似類型,不然沒法將不一樣類型的值加在一塊兒。JavaScript 在進行轉換時遵循一個預約義的規則:cdn

在表達式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 代碼的原理了吧?發揮你的想象,看還能寫出其餘什麼來?好比,今天是聖誕節,來個 「Merry Christmas」?

Anyway,Merry Christmas!

本文根據https://javascript.christmas/2019/17翻譯整理,加了一些本身的內容

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

微信公衆號:1024譯站
相關文章
相關標籤/搜索