JavaScript類型轉(zhuǎn)換深度學(xué)習(xí)
JavaScript 是一門弱類型語(yǔ)言,剛接觸的時(shí)候感覺(jué)方便快捷(不需要聲明變量類型了耶!),接觸久了會(huì)發(fā)現(xiàn)它帶來(lái)的麻煩有的時(shí)候不在預(yù)期之內(nèi)
呵呵一笑,哪有這么夸張,可能有人看過(guò)這樣一段代碼
- [][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+![]+[![]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()
這個(gè)占了好大的篇幅哈 3167 個(gè)字符,粘貼到瀏覽器的 Console 控制臺(tái),直接彈出了 orange,隨叫隨到有不有
對(duì)于不知道原理出處的給大家一個(gè)地址:http://www.jsfuck.com/
JSFuck 的變態(tài)程度達(dá)到了***,因?yàn)樗睦砟钍?Write any JavaScript with 6 Characters: []()!+
或許又有人說(shuō):這個(gè)只是搞怪的吧,實(shí)際誰(shuí)這么寫代碼啊
說(shuō)的沒(méi)錯(cuò),當(dāng)一段代碼變得晦澀難懂的時(shí)候,甚至到上文的混亂字符(天書),卻能實(shí)現(xiàn)任意功能這就變得不可預(yù)期,也就是說(shuō) JS 代碼的安全性沒(méi)有保障
當(dāng)然本文不會(huì)研究這些無(wú)意義的字符原理是怎么實(shí)現(xiàn)的因?yàn)槿思业?Github 文檔已經(jīng)描述的特別全面了,感興趣的可以研究下:https://github.com/aemkei/jsfuck
我們聊一聊每天能看到用到的方法底層是怎么解析的,熟知轉(zhuǎn)換分成兩種一種是隱式轉(zhuǎn)換,另一種是強(qiáng)制的類型轉(zhuǎn)換
隱式轉(zhuǎn)換
當(dāng)遇到以下幾種情況,JavaScript會(huì)自動(dòng)轉(zhuǎn)換數(shù)據(jù)類型:
- 不同類型的數(shù)據(jù)進(jìn)行互相運(yùn)算
- 對(duì)非布爾值類型的數(shù)據(jù)求布爾值
- 對(duì)非數(shù)值類型的數(shù)據(jù)使用一元運(yùn)算符(即 "+" 和 "-")
隱式轉(zhuǎn)換為 Boolean
大多數(shù)在做 if 判斷時(shí)會(huì)用到,這里只需記住六個(gè)轉(zhuǎn)換為 false,其它全部為 true
- null
- undefined
- NaN
- ''
- -0
- +0
隱式轉(zhuǎn)換為 String
字符串的自動(dòng)轉(zhuǎn)換,主要發(fā)生在加法運(yùn)算時(shí)。當(dāng)一個(gè)值為字符串,另一個(gè)值為非字符串,則后者轉(zhuǎn)為字符串。
- '1' + 2 // '12'
- '1' + true // "1true"
- '1' + false // "1false"
- '1' + {} // "1[object Object]"
- '1' + [] // "1"
- '1' + function (){} // "1function (){}"
- '1' + undefined // "1undefined"
- '1' + null // "1null"
隱式轉(zhuǎn)換為 Number
除了加法運(yùn)算符有可能把運(yùn)算子轉(zhuǎn)為字符串,其他運(yùn)算符都會(huì)把兩側(cè)的運(yùn)算子自動(dòng)轉(zhuǎn)成數(shù)值
- '5' - '2' // 3
- '5' * '2' // 10
- true - 1 // 0
- false - 1 // -1
- '1' - 1 // 0
- '5' * [] // 0
- false / '5' // 0
- 'abc' - 1 // NaN
- +'abc' // NaN
- -'abc' // NaN
- +true // 1
- -false // 0
隱式轉(zhuǎn)換的基礎(chǔ)表現(xiàn)都在這了,強(qiáng)調(diào)的是這些轉(zhuǎn)換的背后都伴隨著強(qiáng)制轉(zhuǎn)換,使用 Boolean、Number 和 String,下面重點(diǎn)講一下強(qiáng)制轉(zhuǎn)換的原理
強(qiáng)制轉(zhuǎn)換
看到上面例子也許你已經(jīng)有些許疑問(wèn)了,比如上面的這個(gè) '1' + {} 怎么就輸出 1[object Object] 了呢
如上面強(qiáng)調(diào)的,你會(huì)猜測(cè)首先執(zhí)行 String({}) 得到 "[object Object]" ,然后再字符串拼接,是的我們總能得到轉(zhuǎn)換背后的實(shí)現(xiàn)原理,其實(shí)真實(shí)原理要比這個(gè)復(fù)雜,見下文
強(qiáng)制轉(zhuǎn)換為 Boolean
這里略過(guò)因?yàn)榕c隱式轉(zhuǎn)換相同,切記 []、{} 都轉(zhuǎn)換成 true
強(qiáng)制轉(zhuǎn)換為 String
基本類型的轉(zhuǎn)換結(jié)果與隱式轉(zhuǎn)換相同,這里說(shuō)一下對(duì)象的轉(zhuǎn)換,加深上面引用例子的解析
對(duì)象轉(zhuǎn)換字符串分成三步
- 先調(diào)用toString方法,如果toString方法返回的是原始類型的值,則對(duì)該值使用String方法,不再進(jìn)行以下步驟
- 如果toString方法返回的是復(fù)合類型的值,再調(diào)用valueOf方法,如果valueOf方法返回的是原始類型的值,則對(duì)該值使用String方法,不再進(jìn)行以下步驟
- 如果valueOf方法返回的是復(fù)合類型的值,則報(bào)錯(cuò)
再分解這個(gè)例子
- String({})
- // "[object Object]"
上面代碼相當(dāng)于下面這樣
- String({}.toString())
- // "[object Object]"
如果 toString 方法和 valueOf 方法,返回的都不是原始類型的值,則 String 方法報(bào)錯(cuò)
- var obj = {
- valueOf: function () {
- console.log("valueOf");
- return {};
- },
- toString: function () {
- console.log("toString");
- return {};
- }
- };
- String(obj)
- // TypeError: Cannot convert object to primitive value
我們不難看出可以對(duì) toString 方法和 valueOf 方法進(jìn)行改寫,測(cè)試其先后運(yùn)行的順序也簡(jiǎn)單的多
- String({toString:function(){return 3;}})
- // "3"
- String({valueOf:function (){return 2;}})
- // "[object Object]"
- String({valueOf:function (){return 2;},toString:function(){return 3;}})
- // "3"
結(jié)果表示toString方法先于valueOf方法執(zhí)行
強(qiáng)制轉(zhuǎn)換為 Number
基本類型轉(zhuǎn)換如下
- Number("123") // 123
- Number("123abc") // NaN
- Number("") // 0
- Number(false) // 0
- Number(undefined) // NaN
- Number(null) // 0
對(duì)象轉(zhuǎn)換一樣要復(fù)雜些,與 String 唯一不同的就是 valueOf 方法在前, toString 方法在后,其它不贅述見上文例子。
isNaN() 并不陌生,isNaN({}) //true 的內(nèi)在轉(zhuǎn)換過(guò)程是相同的
總結(jié)
其它的轉(zhuǎn)換原則還有很多,看到這我們還是不能解釋文章開始的代碼轉(zhuǎn)換的過(guò)程,掌握這些更多是保證正常書寫代碼規(guī)避錯(cuò)誤的發(fā)生,十分好奇的可以研究下比較特殊的轉(zhuǎn)化原則,還有好多好多。