JavaScript這門(mén)語(yǔ)言的類(lèi)型系統(tǒng)從來(lái)沒(méi)有它表面看起來(lái)的那樣和善,雖然比起Java、C#等一眾強(qiáng)類(lèi)型語(yǔ)言,它的弱類(lèi)型使用起來(lái)似乎是如此便利,但正因?yàn)樗鼧O高的自由度,所以才會(huì)衍生出令人摸不著頭腦的荒誕行為。
舉個(gè)例子,雖然我們都知道一個(gè)包含內(nèi)容的字符串會(huì)被認(rèn)為是“真值 Truthy”(因?yàn)槌丝兆址馊魏巫址贘S里都被認(rèn)為真值),但當(dāng)你做如下比較的時(shí)候,你會(huì)得到一個(gè)驚掉下巴的結(jié)果
const a = "18";
const b = true;
a == b // false
什么鬼,一個(gè)被通常理解成真值的值,竟然無(wú)法與布爾真值松散相等?
為了能撥開(kāi)JavaScript類(lèi)型的迷霧,讓頭鐵的我們一點(diǎn)一點(diǎn)理順JavaScript整個(gè)類(lèi)型系統(tǒng)的工作邏輯。
讀者可以根據(jù)自己對(duì)JS類(lèi)型系統(tǒng)的掌握程度,選擇性的閱讀這篇博客
類(lèi)型基礎(chǔ)
JavaScript有以下八大類(lèi)型,除了object類(lèi)型,其他都為基本類(lèi)型
number
string
boolean
null
undefined
object
symbol
bigint
他們的類(lèi)型都可以直接被typeof識(shí)別,特例是
雖然你可能已經(jīng)為這種特例所不解,但其實(shí)這才剛開(kāi)始,大的還在后面
類(lèi)型轉(zhuǎn)換
轉(zhuǎn)換為數(shù)字
JavaScript內(nèi)部有一套抽象的數(shù)字轉(zhuǎn)換機(jī)制叫ToNumber,這套機(jī)制在隱式轉(zhuǎn)換或者部分顯式轉(zhuǎn)換其他類(lèi)型值到數(shù)字時(shí)會(huì)被調(diào)用。雖然你可能會(huì)被惡心到,但我還是要向你介紹這套機(jī)制的規(guī)則為
ToNumber的轉(zhuǎn)換規(guī)則會(huì)在以下情況下使用,當(dāng)然這些情況也可以稱(chēng)作轉(zhuǎn)換數(shù)字的“技巧”,看你怎么理解它了:
沒(méi)錯(cuò),Math.floor(true)、+ true、true * 1都等于1,是不是覺(jué)得很荒誕?
但'5' + 3或者5 + '3'結(jié)果都是字符串'53',因?yàn)?code style="box-sizing: inherit; font-family: Consolas, monospace, sans-serif !important; font-size: 0.935em !important; padding: 0px 5px; line-height: 1.8; margin: 0px 3px; display: inline-block; overflow-x: auto; vertical-align: middle; border-radius: 3px !important; background: rgb(247, 247, 249) !important; color: rgb(34, 34, 34) !important; border: none rgb(255, 255, 255) !important; overflow-wrap: break-word !important;">+在有兩個(gè)操作值,且其中一個(gè)為字符串時(shí),會(huì)直接做字符串拼接。所以雖然+ '3'結(jié)果為數(shù)字3,但5 + '3'的結(jié)果不是8
parseInt()或parseFloat()只接受string類(lèi)型,所以轉(zhuǎn)換規(guī)則與ToNumber轉(zhuǎn)換機(jī)制下的string類(lèi)型情況相似,但是在處理字符時(shí)采取從左到右的掃描直到失敗為止的方法,所以parseInt("123hello")結(jié)果為123
轉(zhuǎn)換為布爾
JavaScript還有一套針對(duì)布爾類(lèi)型的抽象轉(zhuǎn)換機(jī)制叫ToBoolean。因?yàn)閷?duì)于前端邏輯編寫(xiě)來(lái)講,判斷一個(gè)值是否為真實(shí)在太重要了,JavaScript里變量像薛定諤的貓一樣,處于存在與不存在、真和假的中間態(tài),所以我們JS開(kāi)發(fā)者都有一個(gè)奇怪的腦回路,當(dāng)看到一個(gè)字面量值的時(shí)候就開(kāi)始評(píng)估它是“真值”(Trusy Value)還是“假值”(Falsy Value)。
可是,與物理學(xué)里薛定諤的貓現(xiàn)象相反,JavaScript里對(duì)真假值的定義其實(shí)很簡(jiǎn)單,以下的值均為假值:
undefined
null
false
+0、-0、0n和NaN
""
其他均為真值,任何非空字符串、非0數(shù)字、對(duì)象都是真值。
轉(zhuǎn)換其他類(lèi)型值為布爾類(lèi)型的方法:
會(huì)隱式的將其他類(lèi)型值轉(zhuǎn)換為布爾的情況:
恭喜你勇士,讀到這里就代表馬上你就能知道為什么"18" != true了?。。?/p>
等價(jià)性
我們都知道,JS當(dāng)中有松散判斷的弱等價(jià)==,和嚴(yán)格判斷的強(qiáng)等價(jià)===兩種判斷等價(jià)方式。強(qiáng)等價(jià)要求兩個(gè)操作值必須為同一類(lèi)型,且值本身也相等,其行為非常容易預(yù)測(cè)。弱等價(jià)在比較值是否相等前會(huì)嘗試做一些類(lèi)型轉(zhuǎn)換,盡可能的讓可能為不同類(lèi)型的兩個(gè)值變得可以判斷。
造成文章開(kāi)頭神奇判斷結(jié)果的原因就在弱等價(jià)==時(shí)的類(lèi)型轉(zhuǎn)換策略上,聽(tīng)我將弱等價(jià)的轉(zhuǎn)換規(guī)則為你娓娓道來(lái)
數(shù)字與字符串比較,則將字符串轉(zhuǎn)換為數(shù)字
布爾值與任何其他值作比較,都先將布爾值轉(zhuǎn)換為數(shù)字
null與undefined松散比較結(jié)果相等
對(duì)象與數(shù)字或字符串比較,先調(diào)用對(duì)象的toPrimitive()獲取原始類(lèi)型值后進(jìn)行比較
所以,"18" == true進(jìn)行比較時(shí)
由于布爾值的比較規(guī)則為將布爾值轉(zhuǎn)換為數(shù)字,ToNumber(true)結(jié)果為數(shù)字1,表達(dá)式變?yōu)?code style="box-sizing: inherit; font-family: Consolas, monospace, sans-serif !important; font-size: 0.935em !important; padding: 0px 5px; line-height: 1.8; margin: 0px 3px; display: inline-block; overflow-x: auto; vertical-align: middle; border-radius: 3px !important; background: rgb(247, 247, 249) !important; color: rgb(34, 34, 34) !important; border: none rgb(255, 255, 255) !important; overflow-wrap: break-word !important;">"18" == 1
又因?yàn)樽址c數(shù)字比較時(shí)字符串應(yīng)轉(zhuǎn)換為數(shù)字,則ToNumber("18")結(jié)果為數(shù)字18
最后表達(dá)式實(shí)際比較18 == 1,結(jié)果為假false
所以沒(méi)錯(cuò),以下表達(dá)式均為真:"1" == true,"0" == false,false == ""
結(jié)語(yǔ)
了解這個(gè)例子后你可能會(huì)對(duì)JavaScript語(yǔ)言行為設(shè)計(jì)混亂表達(dá)不滿(mǎn),但是上述邊界條件可以通過(guò)引入TypeScript,避免弱等價(jià)判斷轉(zhuǎn)而使用嚴(yán)格等價(jià),在隱式類(lèi)型轉(zhuǎn)換之前使用可預(yù)測(cè)行為的轉(zhuǎn)換方法對(duì)值提前進(jìn)行處理。
轉(zhuǎn)自博客園 https://www.cnblogs.com/camwang/p/18242665 作者CamWang
該文章在 2024/6/14 8:46:25 編輯過(guò)