Skip to content

通过 ES 规范探究 indexOf 与 includes 对于 NaN 的区别

背景

事情的起因是我看到 AI 回复了这句话:includes() 方法使用 === 进行判断,但它与 indexOf() 方法不同,因为 includes() 方法能够正确处理 NaN,而 indexOf() 不能。

indexOf 与 includes 居然还有这么细微的差别,我比较好奇原因,想到了可以去 ES 规范 一探究竟

原因

搜索 indexOf 与 includes 发现他们使用了不同的比较算法:

indexOf

This method compares searchElement to the elements of the array, in ascending order, using the IsStrictlyEqual algorithm, and if found at one or more indices, returns the smallest such index; otherwise, it returns -1𝔽.

这里使用了 IsStrictlyEqual 算法

includes

This method compares searchElement to the elements of the array, in ascending order, using the SameValueZero algorithm, and if found at any position, returns true; otherwise, it returns false.

这里使用了 SameValueZero 算法,他们使用的比较算法是不同的所以造成了这种差异

总结

MDN 的描述上也有相关介绍。

使用 AI 时,如果提示从 ES 规范方面比较,他也是能回答出来的,不得不说,只有提示词写的合适,AI 是真的强大。(我使用的 Kimi

AI 回答的重点:

indexOf 使用严格相等比较(===)来查找元素,这意味着它不能正确处理 NaN。在 JavaScript 中,NaN 是一个特殊的值,它与任何值都不相等,包括它自己(NaN === NaN 返回 false)。因此,当使用 indexOf 搜索 NaN 时,即使数组中存在 NaN,也会返回 -1。

includes 方法的实现细节可以参考 ECMAScript 7 实现规范,它内部采用了 SameValueZero 实现,这使得 includes 不区分 +0 和 -0,并且能够检测到 NaN。

只是这么两个常用的 api 就有这样的细节,所以学习技术还是应该有必要深入原理了解的。

附录

IsStrictlyEqual 算法解释

IsStrictlyEqual 是 ECMAScript 语言规范中定义的一个抽象操作,它用于比较两个值是否严格相等,这与 === 运算符的语义是一致的。下面是这个操作的具体步骤和解释:

  1. 类型比较:首先,它会检查两个值 xy 是否属于相同的类型。如果它们的类型不同(即 SameType(x, y) 返回 false),那么 IsStrictlyEqual 将立即返回 false。这意味着不同类型的值永远不会被认为是严格相等的。

  2. 数值比较

    • 如果 x 是一个数值类型(Number),那么算法会进入数值比较的分支。
    • 在这个分支中,算法会调用 Number::equal(x, y) 方法来比较 xy。这个方法会考虑数值的相等性,包括正零和负零(它们在数值上是相等的),但不包括 NaN(因为 NaN 与任何值,包括它自己,都不相等)。
  3. 非数值比较:如果 x 不是数值类型,那么算法将调用 SameValueNonNumber(x, y) 方法。这个方法会进行更细致的比较,包括:

    • 对于字符串,它会逐个字符比较。
    • 对于对象,它会检查两个对象是否引用同一个对象。
    • 对于 undefinednull,它们被认为是相等的。
    • 对于布尔值,它会检查两个值是否都是 true 或都是 false
    • 对于 NaN,由于 NaN 与任何值都不相等,包括它自己,SameValueNonNumber 会返回 false

SameValueZero 算法解释

他与上面流程基本一致,不同点在于 IsStrictlyEqual 调用 调用 Number::equal(x, y) 方法比较,而 SameValueZero 调用 Number::sameValueZero ( x, y ) 来比较,Number::equal 如果是 NaN 就直接返回 false 了,Number::sameValueZero 如果两个都是 NaN 就返回 true。