一行神奇的javascript代碼
寫本篇文章的緣由是之前群里@墨塵發了一段js代碼,如下:
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
然后讓大家運行,出來的結果讓人有點出乎意料,請看:

太風騷了有木有!如果有人詆毀前端瞧不起js的話,那就可以把這段代碼發給他了~
不過話說回來了,這到底是什么原理呢?為什么一堆符號運算結果竟然能是兩個字符,而且恰巧還是個sb!
其實靠的是js的類型轉化的一些基本原理,本篇就來揭密"sb"是如何煉成的。相信你如果能把這個理清楚了,以后遇到類型轉化之類的題目,就可以瞬間秒殺了。
首先要運用到的第一個知識就是js運算符的優先級,因為這么長一段運算看的人眼花,我們必須得先根據優先級分成n小段,然后再各個擊破。優先級的排列如下表:
優先級從高到低:
運算符 |
說明 |
||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
.[ ] ( ) |
字段訪問、數組索引、函數調用和表達式分組 |
||||||||||||||||||||||||||||||||||
++ -- - ~ ! delete new typeof void |
一元運算符、返回數據類型、對象創建、未定義的值 |
||||||||||||||||||||||||||||||||||
</div>
2. 通過ToNumber()把值轉換成Number,直接看ECMA 9.3的表格http://es5.github.io/#x9.3
規則如下:
3. 通過ToString()把值轉化成字符串, 直接看ECMA 9.8的表格http://es5.github.io/#x9.8
規則如下:
規則就這么多,接下來實踐一下,根據我們上面劃分出的子表達式,一步一步將這個神奇的代碼給執行出來。開工~
先看最簡單的子表達式16:+[]
只有一個操作數[],肯定是轉化 為number了,根據上面的規則2,[]是個數組,object類型,即對象。所以得先調用toPrimitive轉化為原始類型,并且 PreferredType為number,這個參數表示更“傾向于”轉化的類型,這里肯定是number了。然后首先調用數組的valueOf方法,數 組調用valueOf會返回自身,如下:
![]()
這個時候,我們得到一個空串“”,還沒有結束,看上面的規則2描述,繼續調用toNumber,轉化為number類型,如下:
</div>
![]()
大功告成!子表達式16轉化完畢,+[],最終得到0。
來看子表達式15:[~+""]
空串""前面有兩個一元操作符,但是操作數還是只有一個,所以,最終要轉化為的類型是number。看規則2吧,空串調用toNumber得到0。接下來是~,這是個什么東東呢?它是位運算符,作用可以記為把數字取負然后減一,所以~0就是-1 。
別忘了,這個子表達式外頭還包著中括號,所以最終的值為[-1],即一個數組,里面只有一個元素-1.
接下來看子表達式13就簡單了,把15、16求出來的填進去,就變成了這樣:--[-1][0],取數組的第0個元素,然后自減,結果為-2,是不so easy!
繼續往上走,子表達式14: [~+[]]
其實把15、和16的原理用上就非常明顯了,答案[-1]
繼續來求子表達式9,此刻它已變成:-2*[-1],有稍許不一樣,不過沒關系,我們還是按照規則來,運算符是乘號*,當然是做數學運算,那后面的[-1]就得轉化為number,與16的求法類似,過程如下:
①調用toPrimitive,發現是object類型
②調用valueOf,返回自身[-1]
③因為不是原始類型,繼續調用toString,返回"-1"
④"-1"是原始類型了,然后調用toNumber,返回-1
⑤與-2相乘,返回2
子表達式10:~~!+[],不多說了,答案1. 就是從右往左依次一元計算。
有了9和10,我們來到了子表達式4,此刻它已經長這樣了:2+1, 好,我不多說了。
繼續 看表達式7:!(~+[]),~+[]=-1,這個根據上面已經知道了,那!-1是什么呢?這里要說一下這個感嘆號,它是邏輯取非的意思,會把表達式轉化 為布爾類型,轉化規則和js的Truthy和Falsy原則是一樣的,后面跟數字的,除0以外都為false,后面跟字符串的,除空串以外都為 false。這里的!-1當然就是false了。
接下來這個表達式3:false+{}有點關鍵。一個布爾加一個對象,那這個{}應該先轉化為原始類型,流程如下:
①調用toPrimitive,發現是object類型
②調用valueOf,返回自身{},
③不是原始類型,調用toString,返回"[object Object]"
④false與"[object Object]"相加,false先轉化為字符串"false"
⑤相加得結果"false[object Object]"
知道了表達式3和4,我們就可以來看表達式1了,此時它是這樣的:"false[object Object]"[3],因為這個[]可以取字符串的子字符,像charAt一樣,所以得到了結果"s"
經過上面艱難的流程,我們拿到了字符"s",也就是那張圖的左半邊,剩下的那個"b",相同的原理可以搞出來,我這里就不一一演示了,留給你練練吧~
回顧一下這個過程其實也不復雜,只是有一些需要重復勞動的,只要你掌握了運算的優先級,能把大串分解成一個個小串,然后運用類型轉化的知識挨個處理就搞定了。怎么樣,看到這里你還覺得神奇嗎?
如果有人瞧不起js,請把這段代碼發給他,如果他想知道答案,請把本文發給他~
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
來自:http://www.cnblogs.com/lvdabao/p/4280518.html 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
|