Array.prototype.includes S4
- 阶段: Stage 4
- 状态: 已完成
- ECMAScript 版本: ES2016
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案添加了 Array.prototype.includes 方法,用于判断数组是否包含某个元素,解决了常见 indexOf 模式无法表达意图且对 NaN 处理不当的问题。该方法使用 SameValueZero 比较,并包含 fromIndex 参数以保持一致性。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
Array.prototype.includes 提案
状态
该提案正式处于 TC39 流程 的第 4 阶段,并正在被整合到规范中。
该提案最初为 Array.prototype.contains,但该名称 不兼容 Web。根据 2014 年 11 月的 TC39 会议,String.prototype.contains 和 Array.prototype.contains 的名称均改为 includes 以避开此问题。
动机
在使用 ECMAScript 数组时,通常需要判断数组是否包含某个元素。常见的模式是:
还有其他各种写法,例如 arr.indexOf(el) >= 0,甚至 ~arr.indexOf(el)。
这些模式存在两个问题:
- 它们未能“表达你的意图”:你没有问数组是否包含元素,而是询问该元素在数组中首次出现的索引,然后通过比较或位运算来确定你的实际问题的答案。
- 它们对
NaN无效,因为indexOf使用严格相等比较,因此[NaN].indexOf(NaN) === -1。
拟议解决方案
我们提议添加 Array.prototype.includes 方法,使得上述模式可以重写为:
这与上述语义几乎相同,不同之处在于它使用 SameValueZero 比较算法而不是严格相等比较,从而使 [NaN].includes(NaN) 为真。
因此,该提案解决了现有代码中的两个问题。
我们还添加了一个 fromIndex 参数,与 Array.prototype.indexOf 和 String.prototype.includes 保持一致。
常见问题
为什么使用 includes 而不是 has?
如果你调查现有 API,has 用于概念上的“键”,而 includes 用于概念上的“值”。即:
- 键值映射中的键:
Map.prototype.has(key),WeakMap.prototype.has(key),Reflect.has(target, propertyKey) - 集合,其元素在概念上既是键又是值:
Set.prototype.has(value),WeakSet.prototype.has(value),Reflect.Loader.prototype.has(name) - 字符串,在概念上是从索引到码点的映射:
String.prototype.includes(searchString, position)
这里与 String 的一致性最好,而不是与 Map 或 Set。
Web 上有一些类似数组的类,如 DOMStringList 和 DOMTokenList,它们具有名为 contains 的方法,语义与我们的 includes 相同。不幸的是,与这些保持一致是不兼容 Web 的,如前所述;我们将不得不接受这种不一致。
但是 String.prototype.includes 操作的是字符串,而不是字符!?
是的,那是正确的。最好的理解方式是,String.prototype.indexOf 和 String.prototype.includes 在单个字符的特殊情况下表现得像它们在 Array.prototype 中的对应方法。但是字符串版本也可以用于更长的字符串的更一般情况。
因此,String.prototype.includes 和 Array.prototype.includes 之间的关系与 String.prototype.indexOf 和 Array.prototype.indexOf 之间的关系相同。
为什么选择 SameValueZero?
当前 ES6 草案中有四种相等算法:
- 抽象相等比较(
==) - 严格相等比较(
===):由Array.prototype.indexOf、Array.prototype.lastIndexOf和case匹配使用 - SameValueZero:由
%TypedArray%和ArrayBuffer构造函数以及Map和Set操作使用 - SameValue:在其他所有地方使用
(但请注意,大多数使用 SameValue 的地方都可以替换为 SameValueZero,因为那些地方通常不比较原始值,或者至少不比较数字。)
使用抽象相等比较显然是疯狂的。使用 SameValue 也不是一个好主意,原因与 Map 和 Set 不使用它的原因相同。(简而言之:-0 可以通过算术运算相当容易地潜入你的代码,但你几乎总是希望 -0 与 +0 被视为相同,因此区分它们只会导致无谓的失败。)这留下了严格相等比较和 SameValueZero 作为两种可能性。
SameValueZero 通常是更好的选择,因为它允许你检测数组是否包含 NaN。支持严格相等比较的论据归结为与 Array.prototype.indexOf 的“错误兼容”。但是 Array.prototype.includes 的目的之一就是引导用户避免这些类型的错误。
这从 Array.prototype.indexOf 重构到 Array.prototype.includes 带来了一点风险:它们对于包含 NaN 的数组确实会表现不同。然而,这种重构更有可能使代码变得_更少_错误,而不是引起问题。引入一个新方法,并伴随适当的信息传递,应该会有所帮助。
类型化数组
与所有非变异数组方法一样,我们也在 %TypedArray%.prototype 上安装此方法。