JavaScript 中的各种比较

2025年01月14日00

JavaScript 中存在各种比较,例如常见的 ==(loosely equal) ===(strictly equal) Object.is isNaN Number.isNaN 等等, 这些操作符或者函数都可以用来比较/判断。本文继续根据 ECMA规范 来学习和比较他们之间的差异。

SameType 和 SameValue

在了解 JavaScript 提供的这些比较/判断方法前,我们需要了解其内部提供的 SameType(x, y)SameValue(x, y) 两个抽象方法。

规范中关于SameType和SameValue的描述

不难看出 SameType 是完全的对类型进行对比,如果类型一致则返回 true,否则返回 false。 对于 SameValue 则是在 SameType的前提上对具体的值进行判断,其中对 Number 类型和 非Number 类型做了区分。其中:

  • Number 中是对 NaN +0 -0 一类的特殊值做了处理
  • Number 类型中其实就是对他们的比较做出了规范的定义
规范中关于Number::SameValue的描述
规范中关于SameValueNonNumber的描述

Strictly Equal

规范中关于Strictly Equal的描述

我们首先看严格相等,规范中首先判断的是类型,如果类型不一致,则直接不想等,后续则使用 Numer.equal 对数字类型进行判断。

值得注意的是,Number::equalNumber::sameValue 对于 NaN +0 -0 的判断:

  1. Number::equal 中,一侧为 NaN 则直接为 false,一侧为 +0 一侧为 -0 认为是 true
  2. Number::sameValue 中,两侧同时为 NaN 返回 true,一侧为 +0 一侧为 -0 返回 false 即:sameValue 相对 equal 比较严格。
规范中关于SameValueNonNumber的描述

Loosely Equal

规范中关于isLooselyEqual的描述

宽松相等的话,当类型一致的话判断方式就直接沿用了 Strictly Equal,后续都是对类型不一致时情况的处理。 可以看到大致逻辑是:

  1. undefinednull
  2. 当一侧为 Number / BigInt 时,另一侧则会转换成 Number / BigInt 类型
  3. 当一侧为 Boolean 类型的时候,就会将 Boolean 转换成 Number 在重新使用 loosely equal 判断
  4. 如果有一侧为对象的话,则将其会转换成原始值后调用 loosely equal 比较
  5. 对于一侧为 Number 一侧为 BigInt 时,如果一侧不是有限值,则直接返回 false

Object.is

规范中关于Object.is的描述

这里直使用了上面的 SameValue 进行判断,而且也没有其他特殊情况的处理。

isNaN

规范中关于isNaN的描述

首先将传入的参数做了转换处理,然后判断转换后的值是不是 NaN,是则返回 true

提示这里说,判断 X 是否为 NaN 的可靠方法是使用形式为 X !== X 的表达式。当且仅当 XNaN 时,结果才为真。 但是需要先排除 X 是对象的前提(即X需要是原始值)。

Number.isNaN

规范中关于Number.isNaN的描述

还是比较简洁的,需要注意的就是 Notes 提到的,与 isNaN 的区别在于: isNaN 会把参数做一次转换.

至此,总结了这几种比较方式,本质上还是通过 SameType SameValue 及其他底层的抽象方法和一些具体情况 进行处理。需要注意的可能就是一些特殊情况以及特殊情况对于的转换逻辑。