JavaScript 中 == 和 === 的區別

1. 引言

這是在 JavaScript 中用來進行數值和對象對比時常用的操作符,從定義上來看:

  • == :抽象相等,比較時會先進性類型轉換,然後再比較值

  • === :嚴格相等,會比較兩個值的類型和值

測試例子:

console.log('10'==10);  // true
console.log('10'===10); // false

 

2. ECMA 規範

上面的例子只是從最直觀的角度展示兩個操作符的差別,想要從底層原理上來剖析兩者的區別,還需要回歸到 ECMA 的規範,這裏以 ECMAScript 2016/ECMA-262 7th 文檔(即 ES 6 版本)中的內容作爲參考依據:

Type(x) 標識 x 的類型,Type(y) 標識 y 的類型

2.1 Strict Equality Comparison(===)

The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. If Type(x) is different from Type(y), return false.

  2. If Type(x) is Number, then

    • a. If x is NaN, return false.

    • b. If y is NaN, return false.

    • c. If x is the same Number value as y, return true.

    • d. If x is +0 and y is ‐0,return true.

    • e. If x is ‐0 and y is +0, return true.

    • f. Return false.

  3. Return SameValueNonNumber(x, y).

NOTE This algorithm differs from the SameValue Algorithm in its treatment of signed zeroes and NaNs.

第 1、2 點比較容易理解,直接字面翻譯即可:

  1. 如果 Type(x) 和 Type(y) 不同,返回 false;

  2. 如果 Type(x) 是 Number :

    • 假如 x 是 NaN ,返回 false

    • 假如 y 是 NaN ,返回 false

    • 假如 x 的數值與 y 相等,返回 true

    • 假如 x 是 +0 ,y 是 -0 ,返回 true

    • 假如 x 是 -0 ,y 是 +0 ,返回 true

    • 其他情況,返回 false

  3. 返回 SameValueNonNumber(x,y) 的結果

 

SameValueNonNumber

這是計算非 Number 類型 x, y 是否相同的方法,詳細定義如下:

The internal comparison abstract operation SameValueNonNumber(x, y), where neither x nor y are Number values, producestrue or false. Such a comparison is performed as follows:

  1. Assert: Type(x) is not Number.

  2. Assert: Type(x) is the same as Type(y).

  3. If Type(x) is Undefined, return true.

  4. If Type(x) is Null, return true.

  5. If Type(x) is String, then

    • a. If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false.

  6. If Type(x) is Boolean, then

    • a. If x and y are both true or both false, return true; otherwise, return false.

  7. If Type(x) is Symbol, then

    • a. If x and y are both the same Symbol value, return true; otherwise, return false.

  8. Return true if x and y are the same Object value. Otherwise, return false.

  1. 斷言 :Type(x) 不是 Number

  2. 斷言 :Type(x) 和 Type(y) 相同

  3. 假如 Type(x) 是 Undefined ,返回 true

  4. 假如 Type(x) 是 Null ,返回 true

  5. 假如 Type(x) 是 String ,則

    當且僅當 x, y 字符序列相同(長度相同且每個位置上的字符也相同),返回 true ,否則,返回 false

  6. 假如 Type(x) 是 Boolean ,則

    x, y 都爲 true 或都爲 false ,返回 true ,否則,返回 false

  7. 假如 Type(x) 是 Symbol ,則

    當 x, y 具有相同 Symbol 值,返回 true,否則,返回 false

  8. 假如 x 和 y 是同一個對象值,返回 true,否則,返回 false

 

2.2 Abstract Equality Comparison(==)

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. If Type(x) is the same as Type(y), thena. Return the result of performing Strict Equality Comparison x === y.

  2. If x is null and y is undefined, return true.

  3. If x is undefined and y is null, return true.

  4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).

  5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.

  6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.

  7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

  8. If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).

  9. If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x)== y.

  10. Return false.

  1. 假如 Type(x) 和 Type(y) 相同,則

    返回嚴格對比 x===y 的結果

  2. 假如 x 是 Null 而 y 是 Undefined ,返回 true

  3. 假如 x 是 Undefined 而 y 是 Null ,返回 true

  4. 假如 Type(x) 是 Number 而 Type(y) 是 String ,返回 x==ToNumber(y) 的結果

  5. 假如 Type(x) 是 String 而 Type(y) 是 Number ,返回 ToNumber(x)==y 的結果

  6. 假如 Type(x) 是 Boolean ,返回 ToNumber(x)==y 的結果

  7. 假如 Type(y) 是 Boolean ,返回 x==ToNumber(y) 的結果

  1. 假如 Type(x) 是 String 、Number 或 Symbol 其中之一,而 Type(y) 是 Object,則返回 x == ToPrimitive(y) 的結果

  2. 假如 Type(x) 是 Object 而 Type(y) 是 String 、Number 或 Symbol 其中之一,則返回 ToPrimitive(x)==y 的結果

  3. 其他情況,返回 false

 

ToPrimitive

用於將複雜數據類型轉化爲簡單數據類型,或者說轉換爲原始類型(Null, Undefined, Number, String, Boolean 等),詳細定義如下:

The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. The abstract operation ToPrimitive converts its input argument to a non-Object type. If an object is capable of converting to more than one primitive type, it may use the optional hint PreferredType to favour that type. Conversion occurs according to Table 9 :

Input Type Result
Undefined Return input.
Null Return input.
Boolean Return input.
Number Return input.
String Return input.
Symbol Return input.
Object Perform the steps following this table.

When Type(input) is Object, the following steps are taken:

  1. If PreferredType was not passed, let hint be “default”.

  2. Else if PreferredType is hint String, let hint be “string”.

  3. Else PreferredType is hint Number, let hint be “number”.

  4. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).

  5. If exoticToPrim is not undefined, then

    • a. Let result be ? Call(exoticToPrim, input, « hint »).

    • b. If Type(result) is not Object, return result.

    • c. Throw a TypeError exception.

  6. If hint is “default”, let hint be “number”.

  7. Return ? OrdinaryToPrimitive(input, hint).When the abstract operation OrdinaryToPrimitive is called with arguments O and hint, the following steps are taken:

  8. Assert: Type(O) is Object.

  9. Assert: Type(hint) is String and its value is either “string” or “number”.

  10. If hint is “string”, then

    • a. Let methodNames be « “toString”, “valueOf” ».

  11. Else,

    • a. Let methodNames be « “valueOf”, “toString” ».

  12. For each name in methodNames in List order, do

    • a. Let method be ? Get(O, name).

    • b. If IsCallable(method) is true, then

      • i. Let result be ? Call(method, O).

      • ii. If Type(result) is not Object, return result.

  13. Throw a TypeError exception.NOTE When ToPrimitive is called with no hint, then it generally behaves as if the hint were Number. However, objects may over‐ride this behaviour by de ining a @@toPrimitive method. Of the objects de ined in this speci ication only Date objects (see 20.3.4.45) and Symbol objects (see 19.4.3.4) over‐ride the default ToPrimitive behaviour. Date objects treat no hint as if the hint were String.

ToPrimitive 接口的定義其實是 ToPrimitive(input[, PreferredType])

  • 第一個參數input 是必選參數,傳入需要進行轉換的數據

  • 第二個參數 PreferredType 是可選參數,傳入期望的轉換後的數據類型,後面稱之爲 hint

當 input 的類型爲 Null, Undefined, Number, String, Boolean 和 Symbol 這些數據類型時,不需要進行轉換,直接返回。

當 input 是 Object 類型時,則

  1. 假如沒有傳入 PreferredType ,令 hint 爲 “default”

  2. 假如 PreferredType 是 String,令 hint 爲 "string"

  3. 假如 PreferredType 是 Number,令 hint 爲 "number"

  4. 令 exoticToPrim 爲 GetMethod(input, @@toPrimitive) 的返回值,其中 @@toPrimitive 是一個用於將對象轉換成原始值的方法。

  5. 假如 exoticToPrim 不是 undefined ,則:

    • 令 result 爲 Call(exoticToPrim, input, « hint »)

    • 假如 result 類型不是 Object ,直接返回 result

    • 拋出 TypeError 異常

  6. 假如 hint 是 “default” ,令 hint 爲 “number”

  7. 返回 OrdinaryToPrimitive(input, hint) 的值

抽象操作 OrdinaryToPrimitive(O, hint) 執行的順序如下:

  • 斷言:O 的類型是 Object

  • 斷言:hint 的類型必須是 String ,且字符串內容只能是 “number” 和 "string"

  • 假如 hint = “string” ,則

    令 methodNames 爲 « “toString”, “valueOf” »

  • 假如 hint = “number” ,則

    令 methodNames 爲 « “valueOf”, “toString” »

  • 對於 methodNames 每一個 name 依次執行如下操作:

    • 令 method 爲 Get(O, name)

    • 假如 IsCallable(method) 返回 true,則繼續執行:

      • 令 result 爲 Call(method, O)

      • 如果 result 的類型不是 Object ,則返回 result

  • 返回 TypeError 異常

 

看起來上面的過程很複雜,但實際上有用的關鍵信息如下:

  • 假如傳入的 hint 是String ,先判斷 toString 能否調用,再判斷 toString() 的結果,是基本類型才返回,再判斷 valueOf 能否調用,再判斷 valueOf() 的結果,是基本類型才返回,否則報錯。

  • 假如傳入的 hint 是 Number(或者沒有 hint ,默認是 Number ),先判斷 valueOf ,再判斷 toString

  • 對於普通 Object,默認用 hint 爲 Number 的方式來轉換,對於 Date 類型的 Object ,用 hint 爲 String 的方式來轉換

 

3. 其他

上面提到的幾個核心的接口 ToNumberToStringToPrimitive ,也是 JavaScript 隱式裝箱 (隱式類型轉換)操作的核心接口。

在分析如下的題目中:

[]+[]、{}+{}、[]+{}、{}+[]

這其實是 JavaScript 中加法運算相關的邏輯 12.8 Additive Operators

AdditiveExpression : AdditiveExpression + MultiplicativeExpression

  1. 把AdditiveExpression的result賦值給lref

  2. 把GetValue(lref)的結果賦值給lval

  3. 把MultiplicativeExpression的result賦值給rref

  4. 把GetValue(rref)的結果賦值給rval

  5. 把ToPrimitive(lval)的結果賦值給lprim

  6. 把ToPrimitive(lval)的結果賦值給rprim

  7. 如果Type(lprim)和Type(rprim)中有一個是String,則a.把ToString(lprim)的結果賦給lstrb.把ToString(rprim)的結果賦給rstrc.返回lstr和rstr拼接的字符串

  8. 把ToNumber(lprim)的結果賦給lnum

  9. 把ToNumber(rprim)的結果賦給rnum

  10. 返回lnum和rnum相加的數值

 

4. 參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章