JavaScript 中 == 和 === 的区别

释放双眼,带上耳机,听听看~!

1. 引言

这是在 JavaScript 中用来进行数值和对象对比时常用的操作符,从定义上来看:

  • == :抽象相等,比较时会先进性类型转换,然后再比较值

  • === :严格相等,会比较两个值的类型和值

测试例子:


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

 

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:

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

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.

  • Return SameValueNonNumber(x, y).

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

第 1、2 点比较容易理解,直接字面翻译即可:

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

如果 Type(x) 是 Number :

  • 假如 x 是 NaN ,返回 false

    • 假如 y 是 NaN ,返回 false

    • 假如 x 的数值与 y 相等,返回 true

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

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

    • 其他情况,返回 false

  • 返回 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:

Assert: Type(x) is not Number.

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

If Type(x) is Undefined, return true.

If Type(x) is Null, return true.

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.

  • If Type(x) is Boolean, then

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

  • If Type(x) is Symbol, then

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

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

断言 :Type(x) 不是 Number

断言 :Type(x) 和 Type(y) 相同

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

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

假如 Type(x) 是 String ,则

当且仅当 x, y 字符序列相同(长度相同且每个位置上的字符也相同),返回 true ,否则,返回 false

假如 Type(x) 是 Boolean ,则

x, y 都为 true 或都为 false ,返回 true ,否则,返回 false

假如 Type(x) 是 Symbol ,则

当 x, y 具有相同 Symbol 值,返回 true,否则,返回 false

假如 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:

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

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

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

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

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

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

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

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

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

Return false.

假如 Type(x) 和 Type(y) 相同,则

返回严格对比 x===y 的结果

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

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

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

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

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

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

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

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

其他情况,返回 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 :

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:

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

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

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

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

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.

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

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

  • Assert: Type(O) is Object.

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

  • If hint is “string”, then

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

  • Else,

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

  • 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).

    
    
    1
    2
    1  * ii. If Type(result) is not Object, return result.
    2
  • 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 类型时,则

假如没有传入 PreferredType ,令 hint 为 “default”

假如 PreferredType 是 String,令 hint 为 "string"

假如 PreferredType 是 Number,令 hint 为 "number"

令 exoticToPrim 为 GetMethod(input, @@toPrimitive) 的返回值,其中 @@toPrimitive 是一个用于将对象转换成原始值的方法。

假如 exoticToPrim 不是 undefined ,则:

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

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

    • 抛出 TypeError 异常

  • 假如 hint 是 “default” ,令 hint 为 “number”

  • 返回 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)

    
    
    1
    2
    1  * 如果 result 的类型不是 Object ,则返回 result
    2
  • 返回 TypeError 异常

 

看起来上面的过程很复杂,但实际上有用的关键信息如下:

  • 假如传入的 hint 是String ,先判断 toString 能否调用,再判断 toString() 的结果,是基本类型才返回,再判断 valueOf 能否调用,再判断 valueOf() 的结果,是基本类型才返回,否则报错。

  • 假如传入的 hint 是 Number(或者没有 hint ,默认是 Number ),先判断 valueOf ,再判断 toString

  • 对于普通 Object,默认用 hint 为 Number 的方式来转换,对于 Date 类型的 Object ,用 hint 为 String 的方式来转换

 

3. 其他

上面提到的几个核心的接口 ToNumber 、ToString 和 ToPrimitive ,也是 JavaScript 隐式装箱 (隐式类型转换)操作的核心接口。

在分析如下的题目中:


1
2
1[]+[]、{}+{}、[]+{}、{}+[]
2

这其实是 JavaScript 中加法运算相关的逻辑 12.8 Additive Operators

AdditiveExpression : AdditiveExpression + MultiplicativeExpression

把AdditiveExpression的result赋值给lref

把GetValue(lref)的结果赋值给lval

把MultiplicativeExpression的result赋值给rref

把GetValue(rref)的结果赋值给rval

把ToPrimitive(lval)的结果赋值给lprim

把ToPrimitive(lval)的结果赋值给rprim

如果Type(lprim)和Type(rprim)中有一个是String,则a.把ToString(lprim)的结果赋给lstrb.把ToString(rprim)的结果赋给rstrc.返回lstr和rstr拼接的字符串

把ToNumber(lprim)的结果赋给lnum

把ToNumber(rprim)的结果赋给rnum

返回lnum和rnum相加的数值

 

给TA打赏
共{{data.count}}人
人已打赏
安全技术

c++ vector

2022-1-11 12:36:11

安全运维

腾讯互娱崔晓春:运维的下一站在哪里?从马镫说起

2016-12-24 18:32:59

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索