JavaScript “相等” 的二三事

相等不相等?

先来随便举几个🌰 吧~

'0' == true          //?
[1] == [1]           //?
[1] == 1             //?
null == false        //?
null == undefined    //?
NaN === NaN          //?
+0 === -0            //?
Object.is([], [])    //?
Object.is(-0, +0)    //?
Object.is(NaN, NaN)  //?

var arr = [NaN, 0, +0]
arr.indexOf(-0)      //?
arr.indexOf(NaN)     //?
arr.includes(-0)     //?
arr.includes(NaN)    //?

可能 ±0NaN 会纠结一点,还是比较基础的,也许很多人一眼扫过去便知道答案了。我在这里结合官方规范进行了整理,希望能带给你不一样的认识。

预备知识

ECMAScript Language Types

规范传送门。根据最新规范,EcmaScript 一共有7种语言类型:

其中 Object引用数据类型,其它几种则为基本数据类型

ToNumber

规范传送门。任意 EcmaScript 类型转化为 Number 类型

类型 结果
Undefined NaN
Null +0
Boolean true -> 1,false -> +0
Number 不转变
String 空字符串 -> +0,有效的数字 -> 十进制数字,其它 -> NaN
Object 先ToPrimitive(hint Number),再ToNumber
Symbol 抛错,TypeError 错误

ToPrimitive

规范传送门。内部方法,主要功能是将引用数据类型转化为基本数据类型

-0、+0、0 的疑惑

明明日常没什么用,为什么会有±0?

SameValueNonNumber 内部方法

SameValueNonNumber 方法接收两个参数 x 和 y ,其中 x 和 y 都不是 Number 类型,该方法返回 truefalse

主要规则

  1. 断言:x 不是 Number 类型。
  2. 断言:x 和 y 是 相同类型。
  3. 如果 x 是 Undefined 类型,返回 true
  4. 如果 x 是 Null 类型,返回 true
  5. 如果 x 是 String 类型:
    • 如果 x 和 y 长度相同且相应编码单元相同,返回 true
    • 否则返回 false
  6. 如果 x 是 Boolean 类型:
    • 如果 x 和 y 都是true 或者 都是false,返回 true
    • 否则返回 false
  7. 如果 x 是 Symbol 类型:
    • 如果 x 和 y 都是相同 Symbol 值,返回 true
    • 否则返回 false
  8. 如果 x 和 y 指向同一对象,返回 true 。否则返回 false

小结

相同类型比较规则(除Number类型)

  1. 都是 undefined,相等
  2. 都是 null,相等
  3. String 类型中,都是相同字符串,相等
  4. Boolean 类型中,都是 true 或者 都是 false,相等
  5. Symbol 类型中,都是相同 Symbol 值,相等
  6. Object 类型中,引用同一对象,相等

使用

哪些 JavaScript 公开方法采用了 SameValueNonNumber 比较呢?

  1. 公开方法木有
  2. 接着看下去你就会知道,撇开数值类型比较,SameValueNonNumberSameValueSameValueZero=== 的公共方法。

SameValueZero 内部方法

SameValueZero 方法接收两个参数 x 和 y ,其中 x 和 y 是 EcmaScript 任意类型值,该方法返回 truefalse

主要规则

  1. 如果 x 和 y 的类型不同,返回 false
  2. 如果 x 是 Number 类型:
    • 如果 x 和 y 都是 NaN ,返回 true
    • 如果 x 是 -0 ,y 是 +0 ,返回 true
    • 如果 x 是 +0 ,y 是 -0 ,返回 true
    • 如果 x 和 y 数值相等,返回 true
    • 返回 false
  3. 返回 SameValueNonNumber(x, y) 方法的返回值。

小结

  1. 不同类型不相等
  2. Number 类型中:±0 相等。NaN 和 NaN 相等。其它相同数值相等
  3. SameValueNonNumber 比较:
    • 都是 undefined,相等
    • 都是 null,相等
    • String 类型中,都是相同字符串,相等
    • Boolean 类型中,都是 true 或者 都是 false,相等
    • Symbol 类型中,都是相同 Symbol 值,相等
    • Object 类型中,引用同一对象,相等

使用

哪些 JavaScript 公开方法采用了 SameValueNonNumber 比较呢?

  1. Array.prototype.includes
  2. Map.prototype.delete
  3. Map.prototype.has
  4. Map.prototype.set
  5. Set.prototype.delete
  6. Set.prototype.has
  7. Set.prototype.add
  8. ArrayBuffer 和 DataView 部分方法

SameValue 内部方法

SameValue 方法接收两个参数 x 和 y ,其中 x 和 y 是 EcmaScript 中任意类型值,该方法返回 truefalse

主要规则

  1. 如果 x 和 y 的类型不同,返回 false
  2. 如果 x 是 Number 类型:
    • 如果 x 和 y 都是 NaN ,返回 true
    • 如果 x 是 -0 ,y 是 +0 ,返回 false
    • 如果 x 是 +0 ,y 是 -0 ,返回 false
    • 如果 x 和 y 数值相等,返回 true
    • 返回 false
  3. 返回 SameValueNonNumber(x, y) 方法的返回值。

小结

  1. 不同类型不相等
  2. Number 类型中:±0 不相等。NaN 和 NaN 相等。其它相同数值相等
  3. SameValueNonNumber 比较:
    • 都是 undefined,相等
    • 都是 null,相等
    • String 类型中,都是相同字符串,相等
    • Boolean 类型中,都是 true 或者 都是 false,相等
    • Symbol 类型中,都是相同 Symbol 值,相等
    • Object 类型中,引用同一对象,相等

使用

哪些 JavaScript 公开方法采用了 SameValueNonNumber 比较呢?

  1. Object.is
  2. 最新的 ES 规范 中,你会发现许多其它内部方法和公开方法都应用了 SameValue 比较方法,其中大部分也没有涉及数值比较。
  3. 至于为什么是 SameValue 方法,而不是 SameValueZero===。其实我也不知道。。。我个人倾向于认为:SameValue 方法原本在 ES5 规范中便存在了,最新的规范是为了保持规范一致而继续沿用。

=== 严格相等运算

Strict Equality Comparison,x === y,返回 true 或者 false

主要规则

  1. 如果 x 和 y 的类型不同,返回 false
  2. 如果 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) 方法的返回值。

小结

  1. 不同类型不相等
  2. Number 类型中:±0 相等。NaN 和 NaN 不相等。其它相同数值相等
  3. SameValueNonNumber比较:
    • 都是 undefined,相等
    • 都是 null,相等
    • String 类型中,都是相同字符串,相等
    • Boolean 类型中,都是 true 或者 都是 false,相等
    • Symbol 类型中,都是相同 Symbol 值,相等
    • Object 类型中,引用同一对象,相等

使用

哪些 JavaScript 公开方法采用了 === 比较呢?

  1. === 严格相等运算
  2. 左右两边是相同类型的 == 相等运算
  3. switch语句中的case
  4. Array.prototype.indexOf
  5. Array.prototype.lastIndexOf

== 相等运算

Abstract Equality Comparison,x == y,返回 true 或者 false

主要规则

  1. 如果 x 和 y 的类型相同:
    • 返回严格相等运算结果 x === y 。
  2. 如果 x 是 null ,y 是 undefined ,返回 true
  3. 如果 x 是 undefined ,y 是 null ,返回 true
  4. 如果 x 是 Number 类型 ,y 是 String 类型,返回 x == ToNumber(y) 运算结果。
  5. 如果 x 是 String 类型 ,y 是 Number 类型,返回 ToNumber(x) == y 运算结果。
  6. 如果 x 是 Boolean 类型 ,返回 ToNumber(x) == y 运算结果。
  7. 如果 y 是 Boolean 类型 ,返回 x == ToNumber(y) 运算结果。
  8. 如果 x 是 Number、String、Symbol 中任意一个类型 ,y 是 Object 类型,返回 x == ToPrimitive(y) 运算结果。
  9. 如果 y 是 Number、String、Symbol 中任意一个类型 ,x 是 Object 类型,返回 ToPrimitive(x) == y 运算结果。
  10. 返回 false

小结

  1. 相同类型:遵循 === 严格相等比较规则。
  2. null == undefined,相等
  3. 不同类型:
    • 基本数据类型转换为 Number 类型再 == 比较。
    • 引用数据类型执行内部 ToPrimitive方法后再 == 比较。

使用

哪些 JavaScript 公开方法采用了 == 比较呢?

  1. 只有这只 == 相等运算

相等不相等

开头的答案。如果对结果感到好奇,不妨对着上面的过程比对~

'0' == true         // false
[1] == [1]          // false
[1] == 1            // true
null == false       // false
null == undefined   // true
NaN === NaN         // false
+0 === -0           // true
Object.is([], [])   // false
Object.is(-0, +0)   // false。见SameValue
Object.is(NaN, NaN) // true。见SameValue

var arr = [NaN, 0, +0]
arr.indexOf(-0)     // 1。见===
arr.indexOf(NaN)    // -1。见===
arr.includes(-0)    // true。见SameValueZero
arr.includes(NaN)   // true。见SameValueZero

总结

  1. SameValueZeroSameValue===这仨完全差不多嘛!
    • 相同点:
      • 不同类型即不相等。
      • 相同类型遵从SameValueNonNumber规则。
    • 不同点:对±0NaN 的判断上各有不同。
  2. Array.prototype.includes 为代表的SameValueZero
    • ±0 相等
    • NaN 和 NaN 相等
  3. Object.is 为代表的SameValue
    • ±0 不相等
    • NaN 和 NaN 相等
  4. ===Array.prototype.indexOf 为代表的===
    • ±0 相等
    • NaN 和 NaN 不相等
  5. ==
    • 相同类型采用===严格比较。
    • 不同类型会隐式转换:
      • 基本数据类型转换为 Number 类型再 == 比较。
      • 引用数据类型执行 ToPrimitive 转换后再 == 比较。
      • undefined/null 特例。