news 2026/9/11 12:48:10

core-js 中的 ECMAScript Array 全面指南:模块划分、内置方法签名与源码级实现解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
core-js 中的 ECMAScript Array 全面指南:模块划分、内置方法签名与源码级实现解析

core-js 中的 ECMAScript Array 全面指南:模块划分、内置方法签名与源码级实现解析

【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js

导读

本文以 core-js 官方文档 docs/web/docs/features/ecmascript/array.md 为骨架,系统梳理 core-js 对 ECMAScriptArray的完整补丁覆盖范围:从 40+ 个es.array.*模块的划分、内置方法 TypeScript 签名,到Array.from/Array.fromAsync/ 迭代器 / Change-array-by-copy 系列的入口与示例。读完本文,你将掌握 core-js 中 Array 相关能力的按需引入方式、各方法签名与边界语义,并通过 packages/core-js/modules 下的源码理解其实现原理与设计取舍。

一、Array 相关模块总览

core-js 将 ECMAScript 标准中 Array 的每一项能力拆分为独立模块,全部位于 packages/core-js/modules,命名遵循es.array.<feature>约定。原文档列出的模块清单如下(已转换为仓库相对路径):

  • 静态方法:es.array.from、es.array.from-async、es.array.is-array、es.array.of
  • 迭代协议:es.array.iterator(同时承载keys/values/entries/@@iterator)、es.array.includes、es.array.at
  • 查找类:es.array.find、es.array.find-index、es.array.find-last、es.array.find-last-index、es.array.index-of、es.array.last-index-of
  • 遍历类:es.array.every、es.array.some、es.array.for-each、es.array.map、es.array.filter、es.array.reduce、es.array.reduce-right
  • 变形类:es.array.flat、es.array.flat-map、es.array.copy-within、es.array.fill、es.array.reverse、es.array.sort、es.array.slice、es.array.splice、es.array.concat、es.array.push、es.array.unshift、es.array.join
  • Change-array-by-copy(复制变更,不修改原数组):es.array.to-reversed、es.array.to-sorted、es.array.to-spliced、es.array.with
  • 辅助模块:es.array.unscopables.flat、es.array.unscopables.flat-map

值得注意的细节:源码目录中还存在 es.array.species 等配套模块,它们为上述方法提供@@species(子类化时返回正确类型)支持。从源码结构可以推断,core-js 的策略是“一个规范条目对应一个模块”,这使得按需打包时可以精确裁剪到单一方法级别。

二、内置方法 TypeScript 签名解读

原文档给出了完整的签名声明(与 packages/core-js 的实现一一对应),这是理解每个方法参数与返回值的权威速查表:

class Array { at(index: int): any; concat(...args: Array<mixed>): Array<mixed>; // 额外支持 @@isConcatSpreadable 与 @@species copyWithin(target: number, start: number, end?: number): this; entries(): Iterator<[index, value]>; every(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): boolean; fill(value: any, start?: number, end?: number): this; filter(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): Array<mixed>; // 额外支持 @@species find(callbackfn: (value: any, index: number, target: any) => boolean), thisArg?: any): any; findIndex(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): uint; findLast(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): any; findLastIndex(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): uint; flat(depthArg?: number = 1): Array<mixed>; flatMap(mapFn: (value: any, index: number, target: any) => any, thisArg: any): Array<mixed>; forEach(callbackfn: (value: any, index: number, target: any) => void, thisArg?: any): void; includes(searchElement: any, from?: number): boolean; indexOf(searchElement: any, from?: number): number; join(separator: string = ','): string; keys(): Iterator<index>; lastIndexOf(searchElement: any, from?: number): number; map(mapFn: (value: any, index: number, target: any) => any, thisArg?: any): Array<mixed>; // 额外支持 @@species push(...args: Array<mixed>): uint; reduce(callbackfn: (memo: any, value: any, index: number, target: any) => any, initialValue?: any): any; reduceRight(callbackfn: (memo: any, value: any, index: number, target: any) => any, initialValue?: any): any; reverse(): this; // 修复 Safari 12.0 bug slice(start?: number, end?: number): Array<mixed>; // 额外支持 @@species splice(start?: number, deleteCount?: number, ...items: Array<mixed>): Array<mixed>; // 额外支持 @@species some(callbackfn: (value: any, index: number, target: any) => boolean, thisArg?: any): boolean; sort(comparefn?: (a: any, b: any) => number): this; // 现代行为,如稳定排序 toReversed(): Array<mixed>; toSpliced(start?: number, deleteCount?: number, ...items: Array<mixed>): Array<mixed>; toSorted(comparefn?: (a: any, b: any) => number): Array<mixed>; unshift(...args: Array<mixed>): uint; values(): Iterator<value>; with(index: includes, value: any): Array<mixed>; @@iterator(): Iterator<value>; @@unscopables: { [newMethodNames: string]: true }; static from(items: Iterable | ArrayLike, mapFn?: (value: any, index: number) => any, thisArg?: any): Array<mixed>; static fromAsync(asyncItems: AsyncIterable | Iterable | ArrayLike, mapfn?: (value: any, index: number) => any, thisArg?: any): Array; static isArray(value: any): boolean; static of(...args: Array<mixed>): Array<mixed>; } class Arguments { @@iterator(): Iterator<value>; // 仅在 core-js 方法中可用 }

签名背后的关键语义

  • @@isConcatSpreadable@@speciesconcatfiltermapslicesplice的注释标明 core-js 额外补齐了这两个 Symbol 协议。其中@@species保证子类实例调用这些方法时,返回的是子类而非基类Array;对应模块见 es.array.species 与 es.symbol.is-concat-spreadable。
  • reverse(): this的 Safari 12.0 bug 修复:该注释说明 core-js 对某些环境中的异常reverse行为做了强制替换,而不是仅做能力探测后跳过。
  • sort的稳定排序:现代规范要求sort稳定,core-js 在旧环境中补齐稳定排序实现。
  • @@unscopables:新增的实例方法(如attoReversedfindLast等)必须登记进@@unscopables,避免with语句环境中旧代码因方法名遮蔽而行为突变。这是 ES2015 起所有新增数组方法的规范要求。
  • Arguments@@iteratorarguments对象的迭代器只在 core-js 补齐的方法路径中可用,签名注释明确标注了这一约束。

三、Entry Points:按需引入的精确粒度

core-js 支持"整包引入"与"单点引入"两种方式。原文档给出的 Entry Points 模式如下:

core-js(-pure)/es|stable|actual|full/array core-js(-pure)/es|stable|actual|full/array/from core-js(-pure)/es|stable|actual|full/array/from-async core-js(-pure)/es|stable|actual|full/array/of core-js(-pure)/es|stable|actual|full/array/is-array core-js(-pure)/es|stable|actual|full/array(/virtual)/at core-js(-pure)/es|stable|actual|full/array(/virtual)/concat core-js(-pure)/es|stable|actual|full/array(/virtual)/copy-within core-js(-pure)/es|stable|actual|full/array(/virtual)/entries core-js(-pure)/es|stable|actual|full/array(/virtual)/every core-js(-pure)/es|stable|actual|full/array(/virtual)/fill core-js(-pure)/es|stable|actual|full/array(/virtual)/filter core-js(-pure)/es|stable|actual|full/array(/virtual)/find core-js(-pure)/es|stable|actual|full/array(/virtual)/find-index core-js(-pure)/es|stable|actual|full/array(/virtual)/find-last core-js(-pure)/es|stable|actual|full/array(/virtual)/find-last-index core-js(-pure)/es|stable|actual|full/array(/virtual)/flat core-js(-pure)/es|stable|actual|full/array(/virtual)/flat-map core-js(-pure)/es|stable|actual|full/array(/virtual)/for-each core-js(-pure)/es|stable|actual|full/array(/virtual)/includes core-js(-pure)/es|stable|actual|full/array(/virtual)/index-of core-js(-pure)/es|stable|actual|full/array(/virtual)/iterator core-js(-pure)/es|stable|actual|full/array(/virtual)/join core-js(-pure)/es|stable|actual|full/array(/virtual)/keys core-js(-pure)/es|stable|actual|full/array(/virtual)/last-index-of core-js(-pure)/es|stable|actual|full/array(/virtual)/map core-js(-pure)/es|stable|actual|full/array(/virtual)/push core-js(-pure)/es|stable|actual|full/array(/virtual)/reduce core-js(-pure)/es|stable|actual|full/array(/virtual)/reduce-right core-js(-pure)/es|stable|actual|full/array(/virtual)/reverse core-js(-pure)/es|stable|actual|full/array(/virtual)/slice core-js(-pure)/es|stable|actual|full/array(/virtual)/some core-js(-pure)/es|stable|actual|full/array(/virtual)/sort core-js(-pure)/es|stable|actual|full/array(/virtual)/splice core-js(-pure)/es|stable|actual|full/array(/virtual)/to-reversed core-js(-pure)/es|stable|actual|full/array(/virtual)/to-sorted core-js(-pure)/es|stable|actual|full/array(/virtual)/to-spliced core-js(-pure)/es|stable|actual|full/array(/virtual)/unshift core-js(-pure)/es|stable|actual|full/array(/virtual)/values core-js(-pure)/es|stable|actual|full/array(/virtual)/with

语法结构拆解

  • core-jscore-js-pure:前者会污染全局(修改原生Array.prototype),后者只导出新对象、不触碰原型链,适合库作者。两种变体在仓库中分别对应 packages/core-js 与 packages/core-js-pure。
  • 四个集合词es | stable | actual | fulles仅含 ECMAScript 标准能力;stable为已稳定提案;actualfull含义按 core-js 版本约定展开(full覆盖更完整)。每个集合下都有对应的目录结构,例如 packages/core-js/stable/array 目录即包含at.jsto-reversed.jsfrom-async.js等 40 个入口文件。
  • (/virtual)可选段core-js-pure下引入/virtual变体时,方法不会挂载到原型上,而是返回可借用的独立函数(Array.prototype.at的纯函数形式),适合以函数式方式调用且零污染的团队。

四、源码级解析:核心方法的实现原理

1.Array.prototype.at的负数索引语义

es.array.at 的实现完整还原了规范逻辑:

at: function at(index) { var O = toObject(this); var len = lengthOfArrayLike(O); var relativeIndex = toIntegerOrInfinity(index); var k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; return (k < 0 || k >= len) ? undefined : O[k]; }

关键点:负数索引通过len + relativeIndex换算,越界统一返回undefined(而非抛错),且对类数组对象({ 0: 1, length: 1 })同样有效。测试 tests/unit-global/es.array.at.js 验证了at(0.4)at(-0)at(NaN)等边界:小数被取整、NaN视为 0、-0等价于0。模块末尾调用addToUnscopables('at'),将其登记进@@unscopables

2.Array.from:迭代分支与数组分支

入口 es.array.from 先执行checkCorrectnessOfIteration探测:若当前环境原生Array.from迭代行为不正确(INCORRECT_ITERATION为真)则强制替换。真正算法位于 internals/array-from.js:

  • 若目标可迭代且不是"数组配合默认迭代器"这一简单情形,走迭代器分支getIteratorMethod(O)取迭代器,逐个next(),每个元素用createProperty(result, index, value)写入,且全程处理doesNotExceedSafeInteger(超过安全整数抛错并iteratorClose关闭迭代器)与callWithSafeIterationClosing(映射函数抛错时安全关闭迭代器);
  • 否则走数组分支:直接lengthOfArrayLike+ 索引循环;
  • 两种分支都支持mapfnthisArg,且new this()保证子类构造器被正确调用。

3.Array.fromAsync:异步迭代的统一管道

es.array.from-async 通过fails探测原生实现是否存在构造缺陷(对应 WebKit bug 271703 的核心是三种输入的归一化

var usingAsyncIterator = getMethod(items, ASYNC_ITERATOR); var usingSyncIterator = usingAsyncIterator ? undefined : getIteratorMethod(items) || safeArrayIterator; var iterator = usingAsyncIterator ? getAsyncIterator(items, usingAsyncIterator) : new AsyncFromSyncIterator(getIteratorDirect(getIterator(items, usingSyncIterator))); resolve(toArray(iterator, mapfn, A));
  • 输入为AsyncIterable:走getAsyncIterator异步消费;
  • 输入为Iterable(含普通数组):用AsyncFromSyncIterator包装同步迭代器,逐项 await;
  • 输入为类数组(无迭代器):退化为safeArrayIterator(基于Array.prototype.values),即Array.fromAsync({ length: 3 }, ...)也可用;
  • 返回结果始终包在Promise中,映射函数mapfn可返回 Promise,toArray会按序 await。

INCORRECT_CONSTRUCTURING的探测技巧值得留意:它用自定义构造器调用nativeFromAsync,若构造器被调用次数不为 1,说明原生实现没有按规范走new C()路径,从而判定需要 polyfill。

4. 迭代器三兄弟:keys/values/entries

es.array.iterator 用一个模块承载四个方法(含@@iterator)。实现采用内部状态机:setInternalState(this, { type, target, index, kind })next()时自增index,按kind'keys'/'values'/ 默认 entries 元组)产出结果,耗尽后将target置空。模块最后还处理了一个历史问题:V8 早期版本(Chrome 45 时代)values函数名不正确,core-js 在非纯模式下用defineProperty修正其namearguments对象的@@iterator也被赋值为数组迭代器(Iterators.Arguments = Iterators.Array)。

5. Change-array-by-copy:不修改原数组的四个方法

toReversed/toSorted/toSpliced/with的共同特点是返回新数组、原数组不变。以 es.array.to-reversed 为例:

toReversed: function toReversed() { var O = toIndexedObject(this); var len = lengthOfArrayLike(O); var A = new $Array(len); var k = 0; for (; k < len; k++) createProperty(A, k, O[len - k - 1]); return A; }

实现刻意使用new $Array(len)(固定基类)而非@@species,这与规范一致:这组方法的结果始终是普通数组,保证语义可预期。四个方法同样通过addToUnscopables登记,防止with语句中旧变量名冲突。

五、完整示例:从文档到可运行代码

原文档的示例是理解这批 API 的最佳入口,以下全部示例均可直接在安装了 core-js 的环境(import 'core-js/full/array';或按需引入)中运行:

Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3] Array.from({ 0: 1, 1: 2, 2: 3, length: 3 }); // => [1, 2, 3] Array.from('123', Number); // => [1, 2, 3] Array.from('123', it => it ** 2); // => [1, 4, 9] Array.of(1); // => [1] Array.of(1, 2, 3); // => [1, 2, 3] let array = ['a', 'b', 'c']; for (let value of array) console.log(value); // => 'a', 'b', 'c' for (let value of array.values()) console.log(value); // => 'a', 'b', 'c' for (let key of array.keys()) console.log(key); // => 0, 1, 2 for (let [key, value] of array.entries()) { console.log(key); // => 0, 1, 2 console.log(value); // => 'a', 'b', 'c' } function isOdd(value) { return value % 2; } [4, 8, 15, 16, 23, 42].find(isOdd); // => 15 [4, 8, 15, 16, 23, 42].findIndex(isOdd); // => 2 [1, 2, 3, 4].findLast(isOdd); // => 3 [1, 2, 3, 4].findLastIndex(isOdd); // => 2 Array(5).fill(42); // => [42, 42, 42, 42, 42] [1, 2, 3, 4, 5].copyWithin(0, 3); // => [4, 5, 3, 4, 5] [1, 2, 3].includes(2); // => true [1, 2, 3].includes(4); // => false [1, 2, 3].includes(2, 2); // => false [NaN].indexOf(NaN); // => -1 [NaN].includes(NaN); // => true Array(1).indexOf(undefined); // => -1 Array(1).includes(undefined); // => true [1, [2, 3], [4, 5]].flat(); // => [1, 2, 3, 4, 5] [1, [2, [3, [4]]], 5].flat(); // => [1, 2, [3, [4]], 5] [1, [2, [3, [4]]], 5].flat(3); // => [1, 2, 3, 4, 5] [{ a: 1, b: 2 }, { a: 3, b: 4 }, { a: 5, b: 6 }].flatMap(it => [it.a, it.b]); // => [1, 2, 3, 4, 5, 6] [1, 2, 3].at(1); // => 2 [1, 2, 3].at(-1); // => 3 const sequence = [1, 2, 3]; sequence.toReversed(); // => [3, 2, 1] sequence; // => [1, 2, 3] const initialArray = [1, 2, 3, 4]; initialArray.toSpliced(1, 2, 5, 6, 7); // => [1, 5, 6, 7, 4] initialArray; // => [1, 2, 3, 4] const outOfOrder = [3, 1, 2]; outOfOrder.toSorted(); // => [1, 2, 3] outOfOrder; // => [3, 1, 2] const correctionNeeded = [1, 1, 3]; correctionNeeded.with(1, 2); // => [1, 2, 3] correctionNeeded; // => [1, 1, 3]

示例中的关键差异点

  • find/findIndexfindLast/findLastIndex:前者从头部找,后者从尾部找;示例中[1,2,3,4].findLast(isOdd)返回 3 而非 1,正是从尾部扫描的结果。
  • includesvsindexOfincludes使用 SameValueZero 比较(NaN可命中、稀疏洞视为undefined),indexOf使用严格相等(NaN永远找不到、稀疏洞跳过)。示例中的四行对比精准展示了这一差异。
  • flatdepthArg默认值为 1:只展开一层;flat(3)才完全打平四层嵌套。
  • Change-array-by-copy 的不变性:四个示例都验证了调用后原数组不变,这是与reverse/sort/splice等原地方法的核心区别。

Array.fromAsync独立示例

await Array.fromAsync( (async function * () { yield * [1, 2, 3]; })(), i => i ** 2 ); // => [1, 4, 9]

该示例演示了三层能力:消费异步生成器、逐项应用映射函数、最终 await 得到普通数组。Array.fromAsync的对应入口为 packages/core-js/stable/array/from-async.js,单元测试覆盖位于 tests/unit-global/es.array.from-async.js(esnext阶段的对应测试在 tests/unit-global/esnext.array.from-async.js,可依仓库实际版本查阅)。

六、如何在你的项目中按需引入

结合 packages/core-js/stable/array 的目录结构,推荐三种引入策略:

  1. 整集合引入(省心)
    import 'core-js/stable/array'; // 或 full/array 覆盖全部
  2. 按方法引入(省体积)
    import 'core-js/modules/es.array.at.js'; import 'core-js/modules/es.array.to-reversed.js'; import 'core-js/modules/es.array.from-async.js';
  3. 纯函数风格(core-js-pure)
    const at = require('core-js-pure/stable/array/virtual/at'); at([1, 2, 3], -1); // 不污染原型链

注意事项:引入es.array.at这类模块时,最好同时引入对应的@@unscopables登记模块(如 es.array.unscopables.flat),以保证with语句环境下的语义一致;若使用 Babel 的useBuiltIns: 'usage'自动按需注入,这些细节会由编译工具自动处理。

七、测试与验证路径

core-js 对 Array 模块有完善的测试覆盖,可在仓库中继续深入:

  • 单元测试(全局版):tests/unit-global 下每个方法一个文件,例如 tests/unit-global/es.array.at.js、es.array.from.jses.array.to-reversed.jses.array.with.js
  • 纯函数版(core-js-pure)对应测试位于 tests/unit-pure;
  • 测试断言非常严格:不仅验证返回值(assert.same),还验证函数arityname、是否looksNative(尽可能模仿原生实现)、是否不可枚举、严格模式下对null/undefined是否抛TypeError,以及@@unscopables登记是否齐全。

通过对照 packages/core-js/modules 的源码与上述测试,你可以验证文中所述的所有行为(at的取整规则、fromAsync的三路输入归一化、Change-array-by-copy 的new $Array语义等),从而在自己的项目中安全地依赖这些能力。

结语

本文完整继承了 core-js 官方 Array 文档的模块清单、内置方法签名、Entry Points 与全部示例,并结合 packages/core-js/modules 的源码解释了at的索引换算、from的双分支算法、fromAsync的异步迭代管道、迭代器状态机以及 Change-array-by-copy 的复制语义。无论你是需要精确按需引入某个数组方法,还是想理解这些 polyfill 在底层如何处理边界情况,本文列出的模块路径与测试文件都是可以直接对照查阅的起点。

【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 12:47:37

PLC恒压供水系统设计与节能优化实践

1. 恒压供水系统概述 恒压供水系统是现代建筑供水工程中的核心设备&#xff0c;它通过自动调节水泵运行状态&#xff0c;确保管网压力稳定在设定值。我参与过多个大型商业综合体的供水系统改造项目&#xff0c;发现传统供水方式普遍存在压力波动大、能耗高、设备寿命短等问题。…

作者头像 李华
网站建设 2026/9/11 12:45:24

AI文献综述工具:提升学术写作效率的7大解决方案

1. 学术写作的AI革命&#xff1a;为什么需要文献综述工具&#xff1f; 读研时最痛苦的记忆莫过于写文献综述。记得有次为导师的课题连续熬了三个通宵&#xff0c;在PubMed和CNKI上手动筛选了200多篇论文&#xff0c;最后整理出的表格还是被批"缺乏系统性"。现在回看&…

作者头像 李华
网站建设 2026/9/11 12:44:05

YOLO目标检测实战:从原理到工业落地的全流程拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 12:42:22

SpringBoot游泳馆运营管理系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 12:42:17

Agent工程化实战:从核心架构到生产落地的全链路指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 12:42:15

Python开发环境搭建全指南:从安装到VS Code配置与虚拟环境管理

1. 为什么“装个Python”这件小事&#xff0c;值得认真对待 先说点掏心窝的话&#xff1a;我见过太多人在Python环境上栽跟头了。有人下载了安装包双击安装&#xff0c;回头发现 python 命令在终端里根本敲不出来&#xff1b;有人用了一个月的Python&#xff0c;发现电脑里装…

作者头像 李华