33 個 JavaScript 核心概念系列(四): == 與 ===

路某人發表於2019-01-05

原文地址:落明的部落格,轉載請註明出處!

JavaScript 中的 == 與 ===

一、前言

作為一個程式設計師,我想大家在第一次看到 a = b 的語句時一定是懵逼的。後來才知道 = 在程式語言中是用來賦值的,而不是用來判斷兩個值是否相等。

那該怎麼判斷兩個值是否相等呢?在 C 或 java 中,是使用 == 來進行比較,而 JavaScript 就有意思了,除了使用 == , 還加了 ===

二、區別與選擇

  1. 它們有什麼區別?

    具體的區別就一句話:== 在比較時允許進行強制型別轉換,而 === 不允許。

    很多人都擔心 == 做的事情多一些會不會影響比較的速度,說實話,會的,但影響是微秒級別的,完全可以忽略不計。

    不難看出,== 像是 === 的一種更深入的擴充套件,因此,滿足 === 的值一定滿足 ==,反之則不成立。

  2. 該如何選擇使用它們?

    很多人會建議你堅持使用 === 而不使用 ==, 我認為這是不明智的,有些時候,比如說處理後端返回的資料時,你無法保證對方傳來的值到你進行比較的時候還是預期的那樣,此時我覺得完全在可以適當的使用 == 來進行相容。

    總的來說,當你真的確定進行比較的值是型別相同的,那就使用 ===,否則,除了幾種特殊情況,使用 == 並沒有什麼問題 。

三、=== 的比較規則

  1. 基本型別值的比較

    === 的比較規則很簡單,對於非物件型別的值,先判斷兩邊的運算元是否是同一型別,如果是,則進行比較,否則,直接返回 false

    但有兩個例外情況:NaN === NaN+0 === -0

    // 不同型別
    '12' === 12; // false
    'a' === true; // false
    null === '12' // false
    null === undefined; // false
    
    // 同型別
    1 === 1; // true
    'a' === 'a'; // true
    false === false; // true
    null === null; // true
    
    //特殊情況
    NaN === NaN; // false
    +0 === -0 // true
    複製程式碼
  2. 引用型別值的比較

    對於包含引用型別值的比較,仍然會先判斷兩邊的資料型別,如果只有一個是引用型別值,則直接返回 false,如果兩邊均是引用型別值,則會比較他們的引用地址是否一致。

    const a = {
        m: 'string',
        n: 12
    };
    const b = {
        m: 'string',
        n: 12
    };
    const c = 12;
    
    a === b; // false
    a === c ; // false
    
    const d = a;
    a === d; // true
    複製程式碼

四、== 的比較規則

一開始說過了,在使用 == 進行比較時,執行對兩邊的運算元進行強制型別轉換,那麼問題來了,什麼情況下會進行轉換?不同型別的轉換規則又是怎樣?

MDN 中有這樣一張表,用來展示不同型別值進行 == 判斷時的轉換規則。

33 個 JavaScript 核心概念系列(四):  == 與 ===

乍一看可能會覺得很亂,但仍然是可以分幾種情況來概括這些情況。

在具體分析之前,建議先閱讀上一篇文章 顯式 (名義) 與 隱式 (鴨子)型別轉換 ,因為上圖中你能看到有諸如 isFalsy()ToNumber()ToString()ToPrimitive 等抽象方法,使用它們只是為了讓大家知道強制轉換的方向和結果,而這些也都是上一篇文章講到的內容。

  1. UndefinedNull 與其它型別值的比較

    我們看前兩行和前兩列可以發現:它們只和自身及對方相等,與其他型別值比較均返回 false ....這個大概就是傳說中的「黑風雙煞」吧!

    大家可能會看到 Object 有一個 isFalse() 方法,這個方法是用來判斷引數值是否是假值,這個時候大家可能會有疑問了,物件不是都是真值嗎?

    沒錯,document.all 就是一個假值物件,雖然已經被新的 JavaScript 標準廢棄,但你或許會在老的專案中看到它,記住就好。

    由於這倆值的特殊性,後面我們說“其他型別”值的時候是排除這倆型別的。

  2. Number 型別值與其他型別值比較

    除了上面的黑風雙煞,Number 算是相等比較時的大哥,誰想和它比較,就得先轉成 Number 型別。

  3. Boolean 型別值與其他型別值比較

    既然有大哥,肯定得有小弟,而 Boolean 型別值則一馬當先,以身作則,將大哥的原則貫徹到底,堪稱模範小弟!

    其他型別值想和 Boolean 值做比較,Boolean 值搖身一變將自己轉成了 Number 型別,哎,你說,別人能怎麼辦?!

    無論別人怎麼說,Boolean 只想做一隻安靜的舔狗,無怨無悔,一生一世。

  4. Object 型別值與其他型別值比較

    Object 作為 JavaScript 中最會偽裝自己的一種型別,在比較之前誰也摸不透它們的真實身份。也正因為此,它們的日子過得不盡相同。

    在進行比較時,JavaScript 國王會通過 toPrimitive() 方法來揭開他們的真面目,最終你會發現,它們的真實身份可能是任意的一種基本型別。

    因此,在最終比較時,它們也將以真實身份與其他型別值比較。

  5. String 型別值與其他型別值比較

    對於 String 型別值來說,在進行比較時的日子並不好過,畢竟,黑風雙煞惹不起,黑社會說話也得聽,唯一能讓它感受到生活希望的,就是在與 Object 這個變色龍進行比較的時候了。

    只有 ObjecttoPrimitive() 後轉為字串的時候它們可以以字串的規則進行比較,否則,它們就要面臨黑風雙煞或是黑社會。

五、== 正確的使用方法

github 上有位大神總結了下面這張圖:

33 個 JavaScript 核心概念系列(四):  == 與 ===

我們可以將此當做一份參考。

其實在實際的使用過程中,只要我們避免一些特殊的情況,== 的使用還是安全的。

下面就是七種所謂的特殊情況。

"0" == false; // true -- 暈!
false == 0; // true -- 暈!
false == ""; // true -- 暈!
false == []; // true -- 暈!
"" == 0; // true -- 暈!
"" == []; // true -- 暈!
0 == []; // true -- 暈!
複製程式碼

如何避免?兩個原則:

  1. 如果兩邊的值中有 true 或者 false,千萬不要使用 ==。
  2. 如果兩邊的值中有 []、"" 或者 0,儘量不要使用 ==。

六、最後

檢驗大家成果的時候到了,

  1. 仔細想想上面七種特殊情況的產生原因。
  2. 思考下面這些值的比較結果。
[] == ![]; // ?
2 == [2]; // ?
'' == [null] // ?

Number.prototype.valueOf = function() {
 return 3;
};
new Number( 2 ) == 3; // ?

複製程式碼

相關文章