通过 ES 规范探究 indexOf 与 includes 对于 NaN 的区别
背景
事情的起因是我看到 AI 回复了这句话:includes() 方法使用 === 进行判断,但它与 indexOf() 方法不同,因为 includes() 方法能够正确处理 NaN,而 indexOf() 不能。
indexOf 与 includes 居然还有这么细微的差别,我比较好奇原因,想到了可以去 ES 规范 一探究竟
原因
搜索 indexOf 与 includes 发现他们使用了不同的比较算法:
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 算法
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 语言规范中定义的一个抽象操作,它用于比较两个值是否严格相等,这与 ===
运算符的语义是一致的。下面是这个操作的具体步骤和解释:
类型比较:首先,它会检查两个值
x
和y
是否属于相同的类型。如果它们的类型不同(即SameType(x, y)
返回false
),那么IsStrictlyEqual
将立即返回false
。这意味着不同类型的值永远不会被认为是严格相等的。数值比较:
- 如果
x
是一个数值类型(Number
),那么算法会进入数值比较的分支。 - 在这个分支中,算法会调用
Number::equal(x, y)
方法来比较x
和y
。这个方法会考虑数值的相等性,包括正零和负零(它们在数值上是相等的),但不包括NaN
(因为NaN
与任何值,包括它自己,都不相等)。
- 如果
非数值比较:如果
x
不是数值类型,那么算法将调用SameValueNonNumber(x, y)
方法。这个方法会进行更细致的比较,包括:- 对于字符串,它会逐个字符比较。
- 对于对象,它会检查两个对象是否引用同一个对象。
- 对于
undefined
和null
,它们被认为是相等的。 - 对于布尔值,它会检查两个值是否都是
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。