Javascript 中的相等型判断

Javascript

前言

Javascript 提供了三种主要的值比较运算符:

  1. 严格相等(三等)===
  2. 宽松相等(两等)==
  3. Object.is()

除此之外,JavaScript 内部还使用了四种不同的相等算法:

  1. 宽松相等
  2. 严格相等
  3. 同值相等
  4. 零值相等

使用严格相等

严格相等是最常用的比较方式。它在比较两个值之前不会进行隐式类型转换,要求两者的类型和值完全相同。

值得注意的是,数组中的索引查找(如 Array.prototype.indexOf)也使用严格相等进行匹配。

test.ts
console.log(+0 === -0) // true
console.log(NaN === NaN) // false
const array = [NaN]
array.indexOf(NaN) // -1

在上例中,虽然 NaN 不等于自身,但 Array.prototype.indexOf 使用严格相等,因此找不到 NaN

使用宽松相等

宽松相等允许在比较值之前执行隐式类型转换,根据值的类型进行适当的规则处理。以下是宽松相等的一些规则:

  1. 如果操作数类型相等,则按以下方式比较
    • 对象:两个操作数是同一引用返回 true
    • 字符串: 两个操作数字符串相等返回 true
    • 数字: 值相等,-0+0 被视为相同的值,NaN 永远不等于自身
    • 布尔:值相等
    • BigInt:值相等
    • Symbol:两个操作数引用相同的符号返回 true
  2. nullundefined:两个操作数必须都是 nullundefined
  3. 对象与原始值:对象需要进行原始值强制转换1
  4. 两个对象都不是原始值,需要将两个操作数都变成原始值进行比较
console.log(5 == 5) // true
console.log('abc' == 'abc') // true
console.log(null == undefined) // true
console.log(undefined == 0) // false 对于第2点
console.log(true == 1) // true,true 转换为 1
console.log(false == 0) // true,false 转换为 0
console.log(true == 0) // false
console.log(5 == '5') // true,字符串 '5' 转换为数字 5
console.log(5 == 'abc') // false,'abc' 转换失败为 NaN
console.log(5 == 5n) // true,直接按数值比较
console.log(5 == NaN) // false,NaN 不等于任何值

从这些例子中可以看到,宽松相等可能因为隐式类型转换导致一些意想不到的结果,因此在实际开发中应尽量避免使用。

同值相等比较 Object.is

Object.is 提供了一种更精确的比较方式,称为同值相等。与严格相等类似,但有两点区别:

  1. 不会进行隐式类型转换。
  2. 对于 NaN-0/+0 的处理更加精准:
  • NaN 被视为等于自身。
  • -0+0 被视为不相等。
console.log(Object.is(NaN, NaN))
// Expected output: true

console.log(Object.is(-0, 0))
// Expected output: false

零值相等 SameValueZero

类似于同值相等,相对于严格相等 NaN 被视作相等的,同时与同值相等的区别 -0+0 被视作相等的。与此对应的是Array.prototype.includes 采用这种比较方式来检查数组中是否包含某个值2

const array = [NaN, -0]
console.log(array.includes(NaN)) // true
console.log(array.includes(+0)) // true
console.log(array.includes(-0)) // true

使用场景

零值相等被设计为更适合实际场景的算法,特别是在列表操作中,比如检查元素存在性。

何时使用 Object.is

  • 需要对 NaN 进行相等的场景,以及 -0+0 不等的情况
  • 宽松比较涉及隐式转换场景,无法区分类型
  • 严格比较需要同类型,但是无法区分 0 的正负,以及 NaN 被视作不相等
  • 同值比较需要值相等,可以区分 0 的正负,NaN 被视作相等
  • 零值相等类似同值相等,无法区分 0 的正负,NaN 被视作相等
xy=====Object.isSameValueZero
+0-0truetruefalsetrue
NaNNaNfalsefalsetruetrue

总结

严格相等适用于大多数场景,因其行为明确且避免隐式类型转换。 宽松相等存在隐式类型转换,不建议在现代开发中使用。 Object.is 提供了精准的比较方式,适合对 NaN-0/+0 敏感的情况。 零值相等是数组查找和存在性判断的理想选择,避免了 NaN-0/+0 的常见问题。 选择正确的比较方式,不仅能提高代码的准确性和可维护性,还能帮助避免潜在的 Bug。

参考

  1. 强制类型转换

  2. GitHub - tc39/proposal-Array.prototype.includes