曾經對我來說,等于就是等于,所以第一次接觸到 JS 中的三等號時我感到很困惑。大部分編程語言中都有雙等號"==",但沒有三等號"==="。
下面是一個比較官方的雙等號與三等號的差別:
"如果需要判斷運算數是否為同一類型并且值相等,則使用嚴格相等運算符[===]。
否則,使用標準相等運算符[==],它允許你比較兩個運算數的值,即使它們類型不同。"
--MDN web docs
在認為理解它的關鍵就在于了解“類型強制轉換”,它是“動態類型語言” Vanilla JAVAScript 中的一個奇怪的概念。
之所以會有類型強制轉換是因為 JS 想要防御編程錯誤,比如拿一個數字和一個字符串比較:
“當在 JavaScript 中使用雙等號時我們測試的是不嚴格的相等。雙等號還會帶來類型強制轉換。
強制轉換的意思是只有當運算數被轉換為相同類型時它們才能被比較。”
換句話說,由于有類型轉換,不同類型的值可以通過 == 被比較,但一個字符串和一個數字則永遠不會用 === 嚴格相等。
舉個例子:
console.log(Number("7") - 7 == 0); // true console.log(Number("7") - 7 === 0); // true console.log(Number("7") - 7 === Number("0")); // true console.log("7" - 7 == "0"); // true console.log("7" - 7 === "0"); // false
JavaScript 中的真與假
在 JavaScript 中我們有六個代表假的值:false, 0, ""(空字符串),null, undefined 和 NaN(Not A Number),這六個假值互相是什么關系呢?
“將 false,0和""用(==)相比較,結果為真,它們都不嚴格相等;null 和 undefined 只和自己相等;NaN 不與任何假值相等,包括它自己。”
// All permutations of falsy comparisons in JavaScript by Dr. Derek Austin console.log(false == false); // true console.log(false === false); // true console.log(false == 0); // true -- false, 0, and "" are loosely equal console.log(false === 0); // false console.log(false == ""); // true -- false, 0, and "" are loosely equal console.log(false === ""); // false console.log(false == null); // false console.log(false === null); // false console.log(false == undefined); // false console.log(false === undefined); // false console.log(false == NaN); // false console.log(false === NaN); // false console.log(0 == 0); // true console.log(0 === 0); // true console.log(0 == ""); // true -- false, 0, and "" are loosely equal console.log(0 === ""); // false console.log(0 == null); // false console.log(0 === null); // false console.log(0 == undefined); // false console.log(0 === undefined); // false console.log(0 == NaN); // false console.log(0 === NaN); // false console.log("" == ""); // true console.log("" === ""); // true console.log("" == null); // false console.log("" === null); // false console.log("" == undefined); // false console.log("" === undefined); // false console.log("" == NaN); // false console.log("" === NaN); // false console.log(null == null); // true console.log(null === null); // true console.log(null == undefined); // true -- null loosely equals undefined console.log(null === undefined); // false console.log(null == NaN); // false console.log(null === NaN); // false console.log(undefined == undefined); // true console.log(undefined === undefined); // true console.log(undefined == NaN); // false console.log(undefined === NaN); // false console.log(NaN == NaN); // false -- NaN nothing equals NaN, and console.log(NaN === NaN); // false -- NaN doesn't equal itself
雖然 JS 中的假值這么奇葩,但幸好除了這些假值以外所有值都為真:
“在 JavaScript 中,真值就是在布爾類型上下文中所有被認為是"true"的值。所有的值皆為真,除非它被定義為假,比如 false,0,"",null,undefined 和 NaN。”
ECMAScript 中的情況呢?
如果你感興趣的話,下面是 JavaScript 所使用的具體算法。
嚴格相等運算符
嚴格相等運算符(=== 與 !==)使用嚴格相等比較算法來比較兩個運算數:
11.9.6 嚴格相等比較算法 當比較 x===y 時,x 與 y 為值,表達式返回 true 或 false,表達式執行方式如下: 1. 如果 Type(x) 和 Type(y) 不同, 返回 false. 2. 如果 Type(x) 為 Undefined, 返回 true. 3. 如果 Type(x) 為 Null, 返回 true. 4. 如果 Type(x) 為數字,那么 → a. 如果 x 為 NaN, 返回 false. → b. 如果 y 為 NaN, 返回 false. → c. 如果 x 與 y 的數字值相等, 返回 true. → d. 如果 x 為 +0 而 y 為 −0, 返回 true. → e. 如果 x 為 −0 而 y 為 +0, 返回 true. → f. 返回 false. 5. 如果 Type(x) 為字符串,如果 x 與 y 中的字符順序完全相同(長度相同,字符位置相同),則返回 true;否則返回 false. 6. 如果 Type(x) 為布爾類型, 如果 x 與 y 皆為真或假,則返回 true;否則,返回 false. 7. 如果 x 和 y 引用了同一個對象,返回 true;否則返回 false. NOTE — This algorithm d如果fers from the SameValue Algorithm (9.12) in its treatment of signed zeroes and NaNs.
標準相等運算符
標準相等運算符(== 和 !=)使用抽象相等比較算法來比較兩個運算數:
11.9.3 抽象相等比較算法(Abstract Equality Comparison Algorithm) 比較 x == y, x 和 y 為值, 表達式返回 true 或 false. 表達式執行如下: 1. 如果 Type(x) 與 Type(y) 相同, 那么 → a. 如果 Type(x) 為 Undefined, 返回 true. → b. 如果 Type(x) 為 Null, 返回 true. → c. 如果 Type(x) 為數字, 則 → → i. 如果 x 為 NaN, 返回 false. → → ii. 如果 y 為 NaN, 返回 false. → → iii. 如果 x 與 y 數字值相等, 返回 true. → → iv. 如果 x 為 +0 并且 y 為 −0, 返回 true. → → v. 如果 x 為 −0 并且 y 為 +0, 返回 true. → → vi. 返回 false. → d. 如果 Type(x) 是字符串, 如果 x 與 y 中的字符順序完全相同(長度相同,字符位置相同),則返回 true;否則返回 false. → e. 如果 Type(x) 為布爾類型, 如果 x 與 y 皆為真或假,則返回true;否則返回 false. → f. 如果 x 與 y 引用了同一個對象,返回 true;否則返回 false. 2. 如果 x 為 null 并且 y 為 undefined, 返回 true. 3. 如果 x 為 undefined 并且 y 為 null, 返回 true. 4. 如果 Type(x) 為數字并且 Type(y) 為字符串, 返回 x == ToNumber(y) 的結果. 5. 如果 Type(x) 為字符串并且 Type(y) 為數字, 返回 ToNumber(x) == y 的結果. 6. 如果 Type(x) 為布爾類型, 返回 ToNumber(x) == y 的結果. 7. 如果 Type(y) 為布爾類型, 返回 x == ToNumber(y) 的結果. 8. 如果 Type(x) 為數字或字符串,并且 Type(y) 為 Object, 返回 x == ToPrimitive(y) 的結果. 9. 如果 Type(x) 為 Object 并且 Type(y) 為數字或字符串, 返回 ToPrimitive(x) == y 的結果. 10. 返回 false. NOTE 1 — Given the above definition of equality: • String comparison can be forced by: "" + a == "" + b. • Numeric comparison can be forced by: +a == +b. • Boolean comparison can be forced by: !a == !b. NOTE 2 — The equality operators maintain the following invariants: • A != B is equivalent to !(A == B). • A == B is equivalent to B == A, except in the order of evaluation of A and B. NOTE 3 The equality operator is not always transitive. For example, there might be two distinct String objects, each representing the same String value; each String object would be considered equal to the String value by the == operator, but the two String objects would not be equal to each other. For example: • new String("a") == "a" and "a" == new String("a")are both true. • new String("a") == new String("a") is false. NOTE 4 Comparison of Strings uses a simple equality test on sequences of code unit values. There is no attempt to use the more complex, semantically oriented definitions of character or string equality and collating order defined in the Unicode spec如果ication. Therefore Strings values that are canonically equal according to the Unicode standard could test as unequal. In effect this algorithm assumes that both Strings are already in normalized form.
結論
通常來說,我比較喜歡用 === 和 !==,除非碰到了必須使用 == 和 != 的情況,比如檢查空值。
另外,在檢查空值時,牢記 null 和 undefined 使用雙等號比較時是相等的這點很有用。