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