一行能裝逼的JavaScript代碼

一行能裝逼的JavaScript代碼

2016-06-28 野狗

 

一行神奇的JS代碼,當時我就震javascript

 

驚了,這不就是傳說中的ZB神奇麼… … 哈哈。前端

 

寫本篇文章的原因是以前看到了一段js代碼,以下:java

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

而後讓你們運行,出來的結果讓人有點出乎意料,請看:  github

太風騷了有木有!若是有人詆譭前端瞧不起JS的話,那就能夠把這段代碼發給他了~數組

不過話說回來了,這究竟是什麼原理呢?爲何一堆符號運算結果居然能是兩個字符,並且恰巧仍是個sb!微信

其實靠的是js的類型轉化的一些基本原理,本篇就來揭密」sb」是如何煉成的。相信你若是能把這個理清楚了,之後遇到類型轉化之類的題目,就能夠瞬間秒殺了。函數

 

 1   JS運算符的優先級post

 

首先要運用到的第一個知識就是js運算符的優先級,由於這麼長一段運算看的人眼花,咱們必須得先根據優先級分紅n小段,而後再各個擊破。優先級的排列以下表:es5

優先級從高到低:

運算符 說明
.[ ] ( ) 字段訪問、數組索引、函數調用和表達式分組
++ — – ~ ! delete new typeof void 一元運算符、返回數據類型、對象建立、未定義的值
* / % 相乘、相除、求餘數
+ – + 相加、相減、字符串串聯
<< >> >>> 移位
< <= > >= instanceof 小於、小於或等於、大於、大於或等於、是否爲特定類的實例
== != === !== 相等、不相等、全等,不全等
& 按位「與」
^ 按位「異或」
| 按位「或」
&& 邏輯「與」
|| 邏輯「或」
?: 條件運算
OP= 賦值、賦值運算(如 += 和 &=)
, 多個計算

根據此規則,咱們把這一串運算分爲如下16個子表達式:

運算符用紅色標出,有一點可能你們會意識不到,其實中括號[]也是一個運算符,用來經過索引訪問數組項,另外也能夠訪問字符串的子字符,有點相似charAt方法,如:’abcd'[1] // 返回’b’。並且中括號的優先級仍是最高的哦。

 

 2   JS的類型轉化

 

預處理結束,接下來須要運用的就是javascript的類型轉化知識了。咱們先說說什麼狀況下須要進行類型轉化。當操做符兩邊的操做數類型不一致或者不是基本類型(也叫原始類型)時,須要進行類型轉化。先按運算符來分一下類:

  • 減號-,乘號*,確定是進行數學運算,因此操做數需轉化爲number類型。

  • 加號+,多是字符串拼接,也多是數學運算,因此可能會轉化爲number或string

  • 一元運算,如+[],只有一個操做數的,轉化爲number類型

  

下面來看一下轉化規則。

 

1. 對於非原始類型的,經過ToPrimitive() 將值轉換成原始類型

ToPrimitive(input, PreferredType?)

可選參數PreferredType是Number或者是String。返回值爲任何原始值。若是PreferredType是Number,執行順序以下:

  1. 若是input爲primitive,返回

  2. 不然,input爲Object。調用 obj.valueOf()。若是結果是primitive,返回。

  3. 不然,調用obj.toString(). 若是結果是primitive,返回

  4. 不然,拋出TypeError

  5. 若是 PreferredType是String,步驟2跟3互換,若是PreferredType沒有,Date實例被設置成String,其餘都是Number

2. 經過ToNumber()把值轉換成Number,直接看ECMA 9.3的表格http://es5.github.io/#x9.3

規則以下:

 

參數 結果
undefined NaN
null +0
boolean true被轉換爲1,false轉換爲+0
number 無需轉換
string 由字符串解析爲數字。例如,」324″被轉換爲324

 

3. 經過ToString()把值轉化成字符串, 直接看ECMA 9.8的表格http://es5.github.io/#x9.8

規則以下:

參數 結果
undefined 「undefined」
null 「null」
boolean 「true」 或者 「false」
number 數字做爲字符串。好比,」1.765″
string 無需轉換

規則就這麼多,接下來實踐一下,根據咱們上面劃分出的子表達式,一步一步將這個神奇的代碼給執行出來。開工~

 

 3   步步執行

先看最簡單的子表達式16:+[]

只有一個操做數[],確定是轉化爲number了,根據上面的規則2,[]是個數組,object類型,即對象。因此得先調用toPrimitive轉化爲原始類型,而且PreferredType爲number,這個參數表示更「傾向於」轉化的類型,這裏確定是number了。而後首先調用數組的valueOf方法,數組調用valueOf會返回自身,以下:

 

這個時候,咱們獲得一個空串「」,尚未結束,看上面的規則2描述,繼續調用toNumber,轉化爲number類型,以下:

 

大功告成!子表達式16轉化完畢,+[],最終獲得0。 

來看子表達式15:[~+」」]

空串」」前面有兩個一元操做符,可是操做數仍是隻有一個,因此,最終要轉化爲的類型是number。看規則2吧,空串調用toNumber獲得0。接下來是~,這是個什麼東東呢?它是位運算符,做用能夠記爲把數字取負而後減一,因此~0就是-1 。

  

別忘了,這個子表達式外頭還包着中括號,因此最終的值爲[-1],即一個數組,裏面只有一個元素-1.

  

接下來看子表達式13就簡單了,把1五、16求出來的填進去,就變成了這樣:–[-1][0],取數組的第0個元素,而後自減,結果爲-2,是不so easy!

  

繼續往上走,子表達式14: [~+[]] 

其實把1五、和16的原理用上就很是明顯了,答案[-1]

  

繼續來求子表達式9,此刻它已變成:-2*[-1],有稍許不同,不過不要緊,咱們仍是按照規則來,運算符是乘號*,固然是作數學運算,那後面的[-1]就得轉化爲number,與16的求法相似,過程以下:

  1. 調用toPrimitive,發現是object類型

  2. 調用valueOf,返回自身[-1]

  3. 由於不是原始類型,繼續調用toString,返回」-1″

  4. 」-1″是原始類型了,而後調用toNumber,返回-1

  5. 與-2相乘,返回2

  

子表達式10:~~!+[],很少說了,答案1. 就是從右往左依次一元計算。 

有了9和10,咱們來到了子表達式4,此刻它已經長這樣了:2+1, 好,我很少說了。

  

繼續看錶達式7:!(~+[]),~+[]=-1,這個根據上面已經知道了,那!-1是什麼呢?這裏要說一下這個感嘆號,它是邏輯取非的意思,會把表達式轉化爲布爾類型,轉化規則和js的Truthy和Falsy原則是同樣的,後面跟數字的,除0之外都爲false,後面跟字符串的,除空串之外都爲false。這裏的!-1固然就是false了。  

接下來這個表達式3:false+{}有點關鍵。一個布爾加一個對象,那這個{}應該先轉化爲原始類型,流程以下:

  1. 調用toPrimitive,發現是object類型

  2. 調用valueOf,返回自身{},

  3. 不是原始類型,調用toString,返回」[object Object]」

  4. false與」[object Object]」相加,false先轉化爲字符串」false」

  5. 相加得結果」false[object Object]」

 

知道了表達式3和4,咱們就能夠來看錶達式1了,此時它是這樣的:」false[object Object]」[3],由於這個[]能夠取字符串的子字符,像charAt同樣,因此獲得告終果」s」。

 

 4   最後

通過上面艱難的流程,咱們拿到了字符」s」,也就是那張圖的左半邊,剩下的那個」b」,相同的原理能夠搞出來,我這裏就不一一演示了,留給你練練吧~

回顧一下這個過程其實也不復雜,只是有一些須要重複勞動的,只要你掌握了運算的優先級,能把大串分解成一個個小串,而後運用類型轉化的知識挨個處理就搞定了。怎麼樣,看到這裏你還以爲神奇嗎?

  

若是有人瞧不起JS,請把這段代碼發給他,若是他想知道答案,請把本文發給他~

 

 

微信掃一掃
關注該公衆號

相關文章
相關標籤/搜索