教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢(xún)/投訴熱線:400-618-4000

一篇文章教你玩轉(zhuǎn)Js類(lèi)型轉(zhuǎn)換

更新時(shí)間:2018年12月13日14時(shí)34分 來(lái)源:傳智播客 瀏覽次數(shù):

0.前言
  • Object 與Primitive,需要Object轉(zhuǎn)為Primitive
  • String 與 Boolean,需要兩個(gè)操作數(shù)同時(shí)轉(zhuǎn)為Number。
  • String/Boolean 與 Number,需要String/Boolean轉(zhuǎn)為Number。
  • undefined 與 null ,和所有其他值比較的結(jié)果都是false,他們之間==成立
ToPrimitive是指轉(zhuǎn)換為js內(nèi)部的原始值,如果是非原始值則轉(zhuǎn)為原始值,調(diào)用valueOf()和toString()來(lái)實(shí)現(xiàn)。valueOf返回對(duì)象的值:在控制臺(tái),當(dāng)你定義一個(gè)對(duì)象按回車(chē),控制臺(tái)打印的是Object{...},toString()返回對(duì)象轉(zhuǎn)字符串的形式,打印的是"[object Object]"
  • 如果參數(shù)是Date對(duì)象的實(shí)例,那么先toString()如果是原始值則返回,否則再valueOf(),如果是原始值則返回,否則報(bào)錯(cuò)。
  • 如果參數(shù)不是Date對(duì)象的實(shí)例,同理,不過(guò)先valueOf再toString()。
1.一些例子
在瀏覽器控制臺(tái)輸入一些各種運(yùn)算符的組合,會(huì)出現(xiàn)一些有意思的結(jié)果:
![] //false; +[]  // 0 +![]  // 0[]+[] // ""{}+{}//"[object Object][object Object]"{}+[]//0{a:0}+1 // 1[]+{}//"[object Object]"[]+![]//"false"{}+[]//0![]+[] // "false"''+{} //"[object Object]"{}+'' //0[]["map"]+[] //"function map() { }"[]["a"]+[] // "undefined"[][[]] + []// "undefined"+!![]+[] //"1"+!![] //11-{} //NaN1-[] //1true-1//0{}-1 //-1[]==![] //true2.從[]==![]開(kāi)始
我們知道,[]!=[],主要是因?yàn)樗麄兪且妙?lèi)型,內(nèi)存地址不同所以不相等。那么為什么加了一個(gè)!就能劃上等于號(hào)了
符號(hào)的優(yōu)先度
可以參考mdn上的這個(gè)匯總表格: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table 可以看見(jiàn),[]==![]這個(gè)情況下先判斷!再判斷= 給[]取反,會(huì)是布爾值,[]的取反的布爾值就是false
2.1 []的反就是false?常見(jiàn)的一些轉(zhuǎn)換:
非布爾類(lèi)型轉(zhuǎn)布爾類(lèi)型:undefined、null 、0、±0、NaN、0長(zhǎng)度的字符串=》false,對(duì)象=》true 非數(shù)字類(lèi)型轉(zhuǎn)數(shù)字類(lèi)型:undefined=》NaN,null=》0,true=》1,false=》0,字符串:字符串?dāng)?shù)字直接轉(zhuǎn)數(shù)字類(lèi)型、字符串非數(shù)字=》NaN
回到[]==![]的問(wèn)題上,[]也是對(duì)象類(lèi)型(typeof [] == "object"),轉(zhuǎn)為布爾類(lèi)型的![]就是false
2.2 等號(hào)兩邊對(duì)比
我們知道,在比較類(lèi)型的時(shí)候,先會(huì)進(jìn)行各種各樣的類(lèi)型轉(zhuǎn)換。 從開(kāi)頭的表格可以看見(jiàn),他們比較的時(shí)候都是先轉(zhuǎn)換為數(shù)字類(lèi)型。右邊是布爾值false,左邊為一個(gè)空數(shù)組對(duì)象,對(duì)于左邊,先進(jìn)行ToPrimitive操作,先執(zhí)行valueOf([])返回的是[],非原始類(lèi)型,再 [].toString(),返回的是"",那ToPrimitive操作之后,結(jié)果就是""了 最后,左邊""和右邊f(xié)alse對(duì)比,他們?cè)俎D(zhuǎn)換為數(shù)字,就是0 == 0的問(wèn)題了
3.更多玩法3.1 間接獲取數(shù)組方法
我們知道,數(shù)組有自己的一套方法,比如var arr = [1,2];arr.push(1),我們可以寫(xiě)成[1,2].push(1),還可以寫(xiě)成[1,2]['push'](1),那么前面拋出的問(wèn)題就解決了
[]['push'](1) //[1][]["map"] //function map() { [native code] }[]["map"]+[] // "function map() { [native code] }"3.2 間接進(jìn)行下標(biāo)操作3.2.1數(shù)字的獲取
我們可以通過(guò)類(lèi)型轉(zhuǎn)換,獲得0和1兩個(gè)數(shù)字,既然能得到這兩個(gè)數(shù)字,那么也可以得到其他的一切數(shù)字了: +[] === 0; +!![] === 1 那么,+!![]+!![] ===2,+((+![])+(+!![])+[]+(+![]))===10
那么10-1=9也就來(lái)了: +((+![])+(+!![])+[]+(+![]))-!![] ===9 簡(jiǎn)直就是無(wú)所不能
3.2.2 字符串下標(biāo)(![]+[])[+[]] //"f"(![]+[])[+!![]] // "a"
(![]+[])是"false",其實(shí)(![]+[])[+[]] 就相當(dāng)于"false"[0],第一個(gè)字母,就是f 我們就可以從上面的那些獲得單詞的字符串獲得其中的字母了,比如:(![]+[])[+!![]+!![]+!![]] +([]+{})[+!![]+!![]]
掌握基本套路后,我們可以隨心所欲發(fā)揮,在瀏覽器的控制臺(tái)輸入一些符號(hào)的組合,然后回車(chē)看一下我們寫(xiě)的“密碼”會(huì)轉(zhuǎn)換成什么
([][[]] + [])[(+!![] + [] + [] + +![] ) >> +!![]] +$$('*')[~~[]].nodeName[- ~ -+~[]] +(this + [])[+!![]+[] + +[] - !!{} - !!{}] +([]+![])[+!!{} << +!![] -~[]] +({}+{})[+!![] -~[]]4. 兩個(gè)面試題
曾經(jīng)遇到兩個(gè)這種類(lèi)型的面試題:
4.1 (a==1 && a==2 && a==3) 能不能為true
(a==1 && a==2 && a==3)或者(a===1 && a===2 && a===3) 能不能為true? 事實(shí)上是可以的,就是因?yàn)樵?=比較的情況下,會(huì)進(jìn)行類(lèi)型的隱式轉(zhuǎn)換。前面已經(jīng)說(shuō)過(guò),如果參數(shù)不是Date對(duì)象的實(shí)例,就會(huì)進(jìn)行類(lèi)型轉(zhuǎn)換,先valueOf再obj.toString() 所以,我們只要改變?cè)膙alueOf或者tostring方法就可以達(dá)到效果:
var a = {  num: 0,  valueOf: function() {    return this.num += 1  }};var eq = (a==1 && a==2 && a==3);console.log(eq);//或者改寫(xiě)他的tostring方法 var num = 0;Function.prototype.toString = function(){    return ++num;}function a(){}//還可以改寫(xiě)ES6的symbol類(lèi)型的toP的方法var  a = {[Symbol.toPrimitive]: (function (i) { return function(){return  ++i } }) (0)};
每一次進(jìn)行等號(hào)的比較,就會(huì)調(diào)用一次valueOf方法,自增1,所以能成立。 另外,減法也是同理:
var a = {  num: 4,  valueOf: function() {    return this.num -= 1  }};var res = (a==3 && a==2 && a==1);console.log(res);
另外,如果沒(méi)有類(lèi)型轉(zhuǎn)換,是 === 的比較,還是可以的。 在vue源碼實(shí)現(xiàn)雙向數(shù)據(jù)綁定中,就利用了defineProperty方法進(jìn)行觀察數(shù)據(jù)被改變的時(shí)候,觸發(fā)set。 每一次訪問(wèn)對(duì)象中的某一個(gè)屬性的時(shí)候,就會(huì)調(diào)用這個(gè)方法定義的對(duì)象里面的get方法。每一次改變對(duì)象屬性的值,就會(huì)訪問(wèn)set方法 在這里,我們自己定義自己的get方法:
var b = 1Object.defineProperty(window, 'a', {  get:function() { return b++; }})var s = (a===1 && a===2 && a === 3 )console.log(s)
每一次訪問(wèn)a屬性,a的屬性值就會(huì)+1,當(dāng)然還是交換位置就不能為T(mén)RUE了
4.2 完善Cash打印三個(gè)101
要求只能在class里面增加代碼:
class Cash {}const a = new Cash(1)const b = new Cash(100)console.log(`${a.add(b)},${Cash.add(a,b)},${new Cash(a+b)}`) // 101,101,101
首先,三個(gè)輸出結(jié)果是以隱式轉(zhuǎn)換的形式出現(xiàn)的,這是關(guān)鍵之處。 a和b都是new出來(lái)的對(duì)象,由new Cash(a+b)可以看出構(gòu)造函數(shù)傳入的也是兩個(gè)Cash的實(shí)例對(duì)象。那么new出來(lái)的結(jié)果肯定不是簡(jiǎn)簡(jiǎn)單單的一個(gè)object,不然就是被轉(zhuǎn)換成'[object Object]',但是你又不得不以object類(lèi)型出現(xiàn),那就只能魔改隱式轉(zhuǎn)換用到的toString和valueOf
class Cash {    constructor (a) {        this.m = a // 緩存真正的值        this.valueOf = function () {            console.log('value')            return a        }    }    add ($) { // a.add(b)        return this.m + $    }    toString () { // 隱式轉(zhuǎn)換調(diào)用        return this.m    }    static add (v1, v2) { //Cash.add        return v1 + v2    }}END
然而,實(shí)際項(xiàng)目中兩個(gè)數(shù)據(jù)作比較的時(shí)候,我們盡量不要寫(xiě)甚至完全不要寫(xiě)兩個(gè)等號(hào),應(yīng)該寫(xiě)三個(gè)等號(hào),而且js也慢慢有向強(qiáng)類(lèi)型過(guò)渡的趨勢(shì),讓這些騷操作回到我們的個(gè)人收藏里面去吧
0 分享到:
和我們?cè)诰€交談!