core-js 中的 Iterator.prototype.includes:从提案到实现的完整解析

发布时间:2026/9/12 2:38:13
core-js 中的 Iterator.prototype.includes:从提案到实现的完整解析
core-js 中的 Iterator.prototype.includes从提案到实现的完整解析【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js导读Iterator.prototype.includes是 TC39 Iterator Helpers 生态 之外独立推进的一项提案proposal-iterator-includes它让所有内置迭代器包括数组迭代器、字符串迭代器、Map/Set 迭代器以及自定义迭代器都能像数组那样直接进行包含性判断。本文以 core-js 仓库中对应的提案文档为骨架结合esnext.iterator.includes模块源码、单元测试与入口点配置完整讲解该方法的行为语义、skippedElements参数的边界规则、异常处理与迭代器关闭机制并给出可直接运行的代码示例与各入口点的引入方式。读完本文你将能在支持与不支持该提案的环境中准确使用并理解这一 API 的实现细节。提案概览与文档定位Iterator.prototype.includes提案Specification 与 Proposal repo的目标是为%Iterator.prototype%增加一个includes方法使迭代器具备与Array.prototype.includes等价的查询能力——即判断迭代器产生的值序列中是否包含某个元素。与数组方法不同迭代器是**惰性lazy**的一旦元素被消费就无法回退因此该方法采用逐项推进、命中即停的策略并在命中时通过return正确关闭迭代器。在 core-js 中该提案对应的模块为 packages/core-js/modules/esnext.iterator.includes.js其提案状态在 packages/core-js/proposals/iterator-includes.js 中登记命名空间前缀esnext表明它当前仍属于提案阶段stage特性。方法签名与行为语义依据文档其内置签名如下class Iterator { includes(searchElement: any, skippedElements?: number): boolean; }searchElement要查找的目标值skippedElements可选查找前需要跳过消费但不参与比较的元素个数返回值boolean找到返回true否则返回false。从 esnext.iterator.includes.js 的实现可以看到完整的内部流程通过anObject(this)强制this必须是对象非对象直接抛出TypeError校验skippedElementsundefined视为0必须是安全整数或±Infinity否则抛TypeError负数或超过0x1FFFFFFFFFFFFF即Number.MAX_SAFE_INTEGER的整数抛RangeError调用iterate逐项推进迭代器先跳过toSkip个元素再对剩余元素用sameValueZero逐个比较命中即通过stop()中断遍历最终返回stopped标志作为布尔结果。SameValueZero 比较语义元素相等性判断使用SameValueZero抽象操作见 packages/core-js/internals/same-value-zero.jsmodule.exports function (x, y) { return x y || x ! x y ! y; };这意味着NaN视为与自身相等x ! x y ! y分支0与-0视为相等判定成立其余值遵循严格相等语义因此undefined ! null不同对象引用也不相等。可直接运行的示例文档给出了六个核心示例全部可在支持 core-js 的环境中直接运行[1, 2, 3].values().includes(2); // true [1, 2, 3].values().includes(4); // false [NaN].values().includes(NaN); // true [1, 2, 3].values().includes(3, 2); // true [1, 2, 3].values().includes(1, 1); // false逐条解读前两条演示基本查找数组迭代器[1, 2, 3].values()是Iterator实例可直接调用includesNaN示例体现 SameValueZero 语义includes(3, 2)跳过前 2 个元素1、2后在第 3 个位置找到3返回trueincludes(1, 1)跳过1之后序列为2, 3不再有1返回false。更贴近迭代器本质的写法——使用生成器或自定义迭代器function* gen() { yield 1; yield 2; yield 3; } gen().includes(2); // true gen().includes(9); // false const set new Set([a, b]); set.values().includes(a); // trueskippedElements 参数详解skippedElements的取值规则在 esnext.iterator.includes.js 中实现得非常严格传入值行为结果undefined/ 缺省视为0不跳过任何元素正常查找0/-0合法不跳过正常查找正整数跳过对应数量的元素后开始比较正常查找Infinity合法跳过所有元素始终返回false负数 /-Infinity非法抛RangeError大于Number.MAX_SAFE_INTEGER0x1FFFFFFFFFFFFF非法非安全整数抛RangeError非整数、NaN、字符串、布尔、null、对象非法抛TypeError这些边界行为与单元测试 tests/unit-global/esnext.iterator.includes.js 完全对应assert.true(includes.call(createIterator([1, 2, 3]), 3, 2), skippedElements #1); assert.false(includes.call(createIterator([1, 2, 3]), 1, 1), skippedElements #2); assert.true(includes.call(createIterator([1, 2, 3, 2]), 2, 2), skippedElements finds later occurrence); assert.false(includes.call(createIterator([1, 2, 3]), 2, 3), skippedElements skips all); assert.true(includes.call(createIterator([1, 2, 3]), 1, Infinity), skippedElements Infinity); assert.throws(() includes.call(createIterator([1]), 1, 1.5), TypeError, non-integer number); assert.throws(() includes.call(createIterator([1]), 1, -1), RangeError, negative integer); assert.throws(() includes.call(createIterator([1]), 1, 0x20000000000000), RangeError, unsafe integer);注意skippedElements: Infinity时实现通过toSkip ! $Infinity toSkip 0x1FFFFFFFFFFFFF绕过上限校验因此不会抛错而是合法地跳过一切返回false。异常处理与迭代器关闭这是该方法与数组方法最关键的区别迭代器是有状态资源异常与提前退出都必须正确关闭。实现中做了两层保护参数校验失败时关闭迭代器校验skippedElements抛出任何错误后立即执行iteratorClose(this, throw, error)见 esnext.iterator.includes.js将错误通过迭代器的return()通道传播并释放资源命中元素时中断并关闭迭代器iterate以{ IS_ITERATOR: true, INTERRUPTED: true }模式运行命中时调用stop()由iterate内部触发return()完成关闭。对应测试 tests/unit-global/esnext.iterator.includes.js 验证了这一契约const it1 createIterator([1, 2, 3], { return() { this.closed true; return { done: true, value: undefined }; }, }); assert.true(includes.call(it1, 2), found element); assert.true(it1.closed, iterator closes on match); const it2 createIterator([1, 2, 3], { return() { this.closed true; } }); assert.throws(() includes.call(it2, 1, -1), RangeError, negative skippedElements); assert.true(it2.closed, iterator closes on negative skippedElements);实际编码时如果你的迭代器持有文件句柄、数据库游标等资源请务必依赖这一关闭机制来确保资源被释放对不支持return()的旧式迭代器则需在finally中自行兜底。入口点与引入方式文档明确了三类入口点均以仓库根目录为基准定位提案整体入口core-js/proposals/iterator-includes对应文件 packages/core-js/proposals/iterator-includes.js内容仅一行require(../modules/esnext.iterator.includes)用于一次性引入该提案的全部模块import core-js/proposals/iterator-includes;按命名空间细化入口core-js(-pure)/actual|full/iterator/includescore-js/actual/iterator/includes对应 packages/core-js/actual/iterator/includes.js会先加载es.object.to-string、es.iterator.constructor、esnext.iterator.includes等依赖模块再通过entryUnbind(Iterator, includes)导出方法适合只引入该单个 API 的场景core-js/full/iterator/includes对应 packages/core-js/full/iterator/includes.js是对actual路径的再导出module.exports parent属于完整full命名空间前缀core-js-pure表示使用 core-js-pure 这一不污染全局的副本适合库作者与工具链内部使用。使用示例// 只引入该 API不污染全局时用 core-js-pure 前缀 import core-js/actual/iterator/includes; // 或在入口统一引入整个提案 import core-js/proposals/iterator-includes; [1, 2, 3].values().includes(2); // true更常用的做法是配合core-js-builder或 Babel 的useBuiltIns配置按需打包仅引入运行环境缺失的部分避免全量引入导致体积膨胀。实现要点与源码级分析Iterator.prototype.includes在 esnext.iterator.includes.js 中的实现包含几个值得深挖的工程细节零依赖的 SameValueZero相等性判断没有复用数组includes的复杂路径而是直接内联实现兼顾了正确性与性能arguments.length区分显式传参通过arguments.length 1 ? arguments[1] : undefined区分未传与显式传undefined两者最终都等价于不跳过但显式传undefined走的是类型校验分支行为一致安全整数上限校验toSkip 0x1FFFFFFFFFFFFF与toSkip ! $Infinity的组合既允许Infinity这一语义上的无限跳过又拒绝任何超出安全整数范围的普通数值防止循环计数溢出单次遍历的惰性消费整个实现通过internals/iterate单遍推进迭代器命中即停、未命中走完即返回false不会像数组那样预先物化全部元素——这是该方法存在的根本意义。此外仓库对Iterator系列方法drop、take、map、filter、some、every等采用了统一的模块命名与入口绑定模式includes遵循同一套 internals/export 导出约定便于维护与按需打包。兼容性、版本状态与使用建议该 API 属于esnext前缀的提案特性在 core-js 中的支持状态随 packages/core-js/proposals/iterator-includes.js 与 tests/compat/tests.js 中的能力检测条目维护提案进入正式标准ES 规范后会被迁移至es.iterator.includes命名空间使用前建议通过Iterator.prototype.includes的存在性检测或 core-js 的按需引入机制降级避免在未 polyfill 的环境中直接调用导致TypeError对是否包含某元素的流式判断includes是最直接的选择若需找到该元素则应使用Iterator.prototype.find若需全部满足则使用every、some系列方法。小结Iterator.prototype.includes为 JS 迭代器补齐了与数组includes对齐的查询能力同时保留了迭代器惰性消费与及时关闭的语义。core-js 通过 esnext.iterator.includes.js 提供了完整实现其skippedElements的严格校验、SameValueZero 相等性、异常时关闭迭代器等细节均有单元测试 tests/unit-global/esnext.iterator.includes.js 覆盖验证。按本文给出的入口点引入后即可在任意 ES2015 环境中安全使用该提案 API。【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考