JS 中相等性的判断

截止至 ES6,有四种相等判断的算法

  1. 全等 (三等)
    ===
  2. 等于
    ==
  3. 零值相等
    -0 === +0
  4. 同值相等
    -0 !== +0
    NaN === NaN

JS 提供有关相等判断的方法

严格相等

  • ===
  • Strict Equality

    不进行隐匿类型转换 类型相同,值也相同 1 === '1' ? false 1 === 2 ? false

    引用值必须是同一地址 var obj = {}; obj === obj ? true {} === {} ? false

    两个 NaN 或者是 NaN 跟其它值都不相等 NaN === NaN ? false NaN === undefined ? false +0 和 -0 相等 +0 === -0 ? true

    Infinity 与 Infinity 相等 Infinity === Infinity ? true

    +Infinity 与 -Infinity 不相等 +Infinity === -Infinity ? false

面试题

如何定义变量 a,使 a !== a 为 true?
那么 a = NaN

非严格(抽象/非约束)相等

  • ==
  • 学述上叫法:Loose (自由的,不受限制的) Equality
  • 一般英文描术为 Abstract Equaliy
    • 隐匿类型转换 - 等式两边都有可能被隐式类型转换
    • 转换以后还是用严格相等来进行比较 | | 被比较值 B | | | | | | | | —- | —- | —- | —- | —- | —- | —- | —- | | | | Undefined | Null | Number | String | Boolean | Object | | 被比较值 A | Undefined | true | true | false | false | false | IsFalsy(B) | | | Null | true | true | false | false | false | IsFalsy(B) | | | Number | false | false | A === B | A === ToNumber(B) | A === ToNumber(B) | A == ToPrimitive(B) | | | String | false | false | ToNumber(A) === B | A === B | ToNumber(A) === ToNumber(B) | ToPrimitive(B) == A | | | Boolean | false | false | ToNumber(A) === B | ToNumber(A) === ToNumber(B) | A === B | ToNumber(A) == ToPrimitive(B) | | | Object | false | false | ToPrimitive(A) == B | ToPrimitive(A) == B | ToPrimitive(A) == ToNumber(B) | A === B |
  • ToNumber 尝试在比较前将参数转换为数字
  • ToPrimitive 尝试调用 .toString() 和 .valueOf() 方法,将参数转换为原始值(Primitive)
  • 任何对象都与 null / undefined 不相等
    • 特例,除了 窄对象 Narrow Object,目前只有 document.all (废除的),仅仅在 IE10 以前
      • IsFalsy 方法的值为 true
      • typeof docuement.all // “undefined”
      • document.all == undefined // true

严格相等的优点

  • 全等对于结果的预测是更加清晰明确
  • 全等在不隐式类型转换的前提下更快

    注意:但不一等在所有的情况下都使用全等,在特定的情况下使用非严格也是有其意义。 例如在封装一个插件时,需要使用者传入一个参数。可以传入 1 或 ‘1’ 会使其程序更有弹性。

falsy 值(虚值)

  1. false
  2. 0 / +0
  3. -0
  4. 0n
  5. “” / ‘’ / ``
  6. null
  7. undefined
  8. NaN

    同值相等 same-value

    同值相等的意思主要针对于 0 的,
  • -0 !== +0 ```javascript var obj = {};

Object.defineProperty(obj, ‘myZero’, { value: -0, writable: false, configurable: false, enumerable: false, });

// +0/0 抛出异常,不能重新定义myZero属性,而 -0 是可以 // 所以在 Object.defineProperty 中 -0 与 +0 不是相同值 Object.defineProperty(obj, ‘myZero’, { value: +0, }); Object.defineProperty(obj, ‘myZero’, { value: 0, }); Object.defineProperty(obj, ‘myZero’, { value: -0, });

  1. - NaN === NaN
  2. ```javascript
  3. var obj = {};
  4. Object.defineProperty(obj, 'myNaN', {
  5. value: NaN,
  6. writable: false,
  7. configurable: false,
  8. enumerable: false,
  9. });
  10. // 不会抛出异常,NaN === NaN 是同值相等
  11. Object.defineProperty(obj, 'myNaN', {
  12. value: NaN,
  13. });

同值相等的底层实现

是通过 Object.is() 的方法,Object.is() 是 ES6 抛出来的,在 ES5 并没有暴露 JS 引擎的同值相等的方法。

Object.is(v1, v2)

  • ES6 新的 API
  • 判断两个参数是否是同一个值(同值相等的实现)
    • 参数为两个值
    • 返回值为同值相等判断的布尔结果

ES2015 (ES6)

Object.is(undefined, undefined); // true
Object.is(null, null); // true
Object.is('1', '1'); // true

var obj = {};
Object.is(obj, obj); // true 同一个引用
Object.is({}, {}); // false 不同引用

Object.is(1, 1); // true
Object.is(0, 0); // true

// Object.is()判断标准就是同值相等
Object.is(NaN, NaN); // true
Object.is(+0, -0); // false

// 不进行隐式转换
Object.is(1, '1'); // false
x y == === Object.is
undefined undefined true true true
null null true true true
true true true true true
false false true true true
"foo" "foo" true true true
0 0 true true true
+0 -0 true true false
0 false true false false
"" false true false false
"" 0 true false false
"0" 0 true false false
"17" 17 true false false
[1,2] "1,2" true false false
new String("foo") "foo" true false false
null undefined true false false
null false false false false
undefined false false false false
{ foo: "bar" } { foo: "bar" } false false false
new String("foo") new String("foo") false false false
0 null false false false
0 NaN false false false
"foo" NaN false false false
NaN NaN false false true

明显看出 Object.is 与 === 的区别只有 +0与-0比较 和 NaN与NaN比较。

Polyfill

1/+0 === Infinity
1/-0 === -Infinity

如何定义变量a a!==a // true 只有a = NaN

Object.myIs = function(a, b){  
    if(a === b){
      return a !== 0 || 1 / a === 1 / b; // 如果等于0(+0/-0)时,转为Infinity再,实现同值相等
  }
  return a !== a && b !== b; // a不等于a只能是NaN
}

零值相等 same-value-zero

+0 === -0