core-js 中的 ChangeArrayby copy 提案实现toReversed、toSorted、toSpliced、with 的规范解析与实战使用【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js本指南围绕 core-js 对 TC39「ChangeArrayby copy」提案已合入 ECMA-262 标准的完整支持展开覆盖 Array 与 %TypedArray% 两组复制式变更方法的规范签名、每个方法的源码级实现原理、按需引入的 Entry Points以及配套单元测试所验证的边界行为。读完本文你将能在项目中安全、精准地使用这四个返回新数组的方法彻底告别slice().reverse()、slice().sort()这类临时拷贝写法并理解 core-js 是如何在旧环境中补齐这些现代语义的。提案背景为什么要按副本变更传统的Array.prototype.reverse、sort、splice都会原地修改调用者数组。为了不破坏原数据开发者只能先手动拷贝一份再操作const original [3, 1, 2]; const reversed original.slice().reverse(); // 需要临时 slice 一次 const sorted original.slice().sort(); // 同上「ChangeArrayby copy」提案的核心思路是为每个原地方法提供一个以to开头的复制式对应物返回一个新数组/新 TypedArray原对象保持不变。在 core-js 仓库中这一功能已从提案阶段进入标准实现例如 es.array.to-reversed.js 的注释已直接指向 ECMA-262 规范章节https://tc39.es/ecma262/#sec-array.prototype.toreversed说明这些方法在 core-js 中已作为es.*标准模块维护并被 stage/4.js 引入对应 Stage 4 已完成提案的自动启用入口。规范签名Array 与 %TypedArray%提案定义了 8 个方法普通数组 4 个、类型化数组 3 个。原文档给出的 TypeScript 风格签名如下其中%TypedArray%泛指Int8Array、Uint8Array、BigInt64Array等全部类型化数组子类class Array { toReversed(): Arraymixed; toSpliced(start?: number, deleteCount?: number, ...items: Arraymixed): Arraymixed; toSorted(comparefn?: (a: any, b: any) number): Arraymixed; with(index: number, value: any): Arraymixed; } class %TypedArray% { toReversed(): %TypedArray%; toSorted(comparefn?: (a: any, b: any) number): %TypedArray%; with(index: number, value: any): %TypedArray%; }注原文档签名中with的参数写作index: includes应为number类型的笔误规范与 core-js 源码中该参数均为相对索引支持负值详见下文各方法解析。三个方法的核心语义方法等价于原地位操作返回值toReversed()reverse()反转后的新数组toSorted(comparefn?)sort(comparefn?)排序后的新数组toSpliced(start, deleteCount?, ...items)splice(start, deleteCount, ...items)增删元素后的新数组with(index, value)arr[index] value替换单个元素后的新数组注意%TypedArray%只有toReversed/toSorted/with三个标准方法没有标准的toSpliced。这一点在 core-js 的提案入口中体现得非常清楚见下文 Entry Points 章节。Array 系列方法的源码级实现toReversed复制反转es.array.to-reversed.js 的实现非常直观——先按length创建一个新数组再逆序拷贝元素$({ target: Array, proto: true }, { 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; } }); addToUnscopables(toReversed);关键细节用toIndexedObject(this)先做索引化对象转换因此任意类数组对象都能调用测试 es.array.to-reversed.js 专门验证了带length的普通对象目标用createProperty逐个定义元素跳过稀疏空洞与规范要求的 hole 语义一致末尾addToUnscopables(toReversed)将方法名写入Array.prototype[Symbol.unscopables]防止with (array)环境下与旧式变量名冲突测试第 61 行断言了这一点。单元测试还验证了其不可变与非泛型属性assert.notSame(array.toReversed(), array, immutable)确保返回全新数组即使目标对象被篡改constructor[Symbol.species]toReversed依然返回真正的Array实例tests/unit-global/es.array.to-reversed.js。toSorted先拷贝再排序es.array.to-sorted.js 巧妙地复用了原生sort避免重复实现排序算法var sort uncurryThis(getBuiltInPrototypeMethod(Array, sort)); $({ target: Array, proto: true }, { toSorted: function toSorted(compareFn) { if (compareFn ! undefined) aCallable(compareFn); var O toIndexedObject(this); var A arrayFromConstructorAndList($Array, O); return sort(A, compareFn); } });实现要点若传入比较函数先用aCallable校验其可调用性非法参数直接抛TypeErrorarrayFromConstructorAndList($Array, O)把原数组元素完整拷进新数组最后对副本调用 uncurry 过的原生sort原数组不受影响。toSpliced返回 splice 结果的副本这是逻辑最复杂的一个。es.array.to-spliced.js 按参数个数分三种情况计算插入数与实际删除数if (argumentsLength 0) { insertCount actualDeleteCount 0; } else if (argumentsLength 1) { insertCount 0; actualDeleteCount len - actualStart; } else { insertCount argumentsLength - 2; actualDeleteCount min(max(toIntegerOrInfinity(deleteCount), 0), len - actualStart); } newLen doesNotExceedSafeInteger(len insertCount - actualDeleteCount); A $Array(newLen);不传参数复制一份删除 0、插入 0只传start删除从actualStart到末尾的全部元素传全参数deleteCount先toIntegerOrInfinity取整再夹取到[0, len - actualStart]区间新长度通过doesNotExceedSafeInteger做安全整数上限校验防止超长数组导致溢出。随后用三个循环依次填充保留段 → 插入段arguments中的items→ 尾部剩余段最终返回全新的toSpliced结果数组。start同样支持负索引由toAbsoluteIndex处理。with替换单个元素es.array.with.js 实现替换语义var relativeIndex toIntegerOrInfinity(index); var actualIndex relativeIndex 0 ? len relativeIndex : relativeIndex; if (actualIndex len || actualIndex 0) throw new $RangeError(Incorrect index); var A new $Array(len); var k 0; for (; k len; k) createProperty(A, k, k actualIndex ? value : O[k]);索引先取整负数按len index从尾部定位-1即最后一个元素解析后的索引越界 len或 0时抛出RangeError(Incorrect index)新数组在目标索引处写入value其余元素原样复制。值得注意的一个细节是文件头部的INCORRECT_EXCEPTION_ON_COERCION_FAIL检测es.array.with.js它专门探测 Firefox 对索引强转失败时应抛出的异常的实现差异并通过$({ ..., forced: INCORRECT_EXCEPTION_ON_COERCION_FAIL })决定是否强制覆盖原生实现——这正是 core-js 处理引擎行为分歧的典型手法。%TypedArray% 系列方法保留类型的复制操作类型化数组的三个方法由 es.typed-array.to-reversed.js、es.typed-array.to-sorted.js、es.typed-array.with.js 实现共用的关键模式是var A new (getTypedArrayConstructor(O))(len); // 与原数组同类型的全新 TypedArray即通过getTypedArrayConstructor取得调用者的具体类型构造器确保Int16Array#toReversed返回的仍是Int16Array而不是普通Array从类型上兑现按副本变更的承诺。三个方法的实现分别对应复制反转、复制后原生排序、以及带索引替换的复制。TypedArray 版本的with还有两个额外的规范细节es.typed-array.with.jsBigInt 类型适配通过isBigIntArray(O)判断目标是否为 BigInt 类型数组若是则用toBigInt(value)把传入值转成 BigInt否则用value做 Number 强转避免BigInt64Array/BigUint64Array上出现类型错误引擎兼容性探测PROPER_ORDER与THROW_ON_NEGATIVE_FRACTIONAL_INDEX两个探测函数分别检测早期 WebKit 实现中值强转与索引校验的先后顺序以及负小数索引如-0.5应截断为 0 而非抛错的偏差对应 tc39 提案 PR #86 的语义修正一旦检测到偏差即通过forced参数强制覆盖原生实现。Entry Points按需引入的正确姿势原文档给出的完整入口列表如下这也是 README 中 ChangeArrayby copy 一节推荐的引用方式core-js/proposals/change-array-by-copy-stage-4 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)/with core-js/es|stable|actual|full/typed-array/to-reversed core-js/es|stable|actual|full/typed-array/to-sorted core-js/es|stable|actual|full/typed-array/with对各部分的解读聚合入口core-js/proposals/change-array-by-copy-stage-4一次性引入提案全部方法。其真实实现是 proposals/change-array-by-copy-stage-4.js内部逐行require了esnext.array.to-reversed、esnext.array.to-sorted、esnext.array.to-spliced、esnext.array.with、esnext.typed-array.to-reversed、esnext.typed-array.to-sorted、esnext.typed-array.with七个模块。同时该入口已被 stage/4.js 引用因此引入core-js/stage/4即可自动获得全部 Stage 4 能力es|stable|actual|fullcore-js 的四个引入层级esECMAScript 标准、stable含已稳定提案、actual含当前活跃提案、full全部。对应仓库 packages/core-js 下的es/、stable/、actual/、full/目录可按项目对提案激进程度的要求选择(-pure)表示该入口同时适用于core-js与core-js-pure后者不污染全局适合库作者array(/virtual)/virtual/变体返回在任意对象上通过.call使用的方法适合不想修改Array.prototype的场景TypedArray 入口无(virtual)与数组方法不同类型化数组方法以静态导出为主直接挂在各 TypedArray 子类的原型上。若你的项目已经按es.*标准模块精细化引入则可以直接引用core-js/es/array/to-reversed这类单方法入口而esnext.*变体见 proposals/change-array-by-copy.js其中还包含标注了TODO: Remove from core-js4的esnext.typed-array.to-spliced则是提案未定稿时期的遗留命名。实战示例以下用法可直接在支持 core-js 的浏览器或 Node.js 环境中运行以es入口为例// 引入所需 polyfill按需选择入口 // import core-js/es/array/to-reversed; // import core-js/es/array/to-sorted; // import core-js/es/array/to-spliced; // import core-js/es/array/with; // import core-js/es/typed-array/to-reversed; const arr [1, 2, 3]; arr.toReversed(); // [3, 2, 1] arr; // [1, 2, 3]原数组不变 arr.toSorted((a, b) b - a); // [3, 2, 1] arr.toSpliced(1, 1, 4); // [1, 4, 3] arr; // [1, 2, 3] arr.with(1, 9); // [1, 9, 3] arr.with(-1, 0); // [1, 2, 0]负索引从尾部定位TypedArray 同样适用且结果保持原类型const typed new Int8Array([1, 2, 3]); const reversed typed.toReversed(); // Int8Array [3, 2, 1] reversed instanceof Int8Array; // true typed; // Int8Array [1, 2, 3]原视图不变注意事项与边界行为纯函数式承诺四个方法一律返回新对象、绝不改动原数组/原 TypedArray这是与reverse/sort/splice/ 索引赋值最本质的区别索引越界with的索引解析后越界会抛RangeError(Incorrect index)toSpliced的deleteCount会被自动夹取到合法区间而非抛错两者策略不同比较函数校验toSorted传入非函数的comparefn会抛TypeErrorSymbol.unscopablestoReversed、toSorted、toSpliced均通过addToUnscopables登记规避with语句的变量遮蔽问题类数组通用性Array 系列方法通过toIndexedObject/lengthOfArrayLike支持任意类数组目标可通过.call使用TypedArray 系列则严格要求this是 TypedArray 实例aTypedArray校验引擎偏差兜底对于 Firefox 的异常强转差异、WebKit 的负小数索引与值强转顺序问题core-js 通过运行时探测 forced标记强制覆盖保证跨引擎行为一致。小结「ChangeArrayby copy」是 ECMAScript 近年来少有的、以返回副本为设计哲学的标准方法组。core-js 对它的支持横跨标准实现es.array.*、es.typed-array.*模块与提案入口change-array-by-copy-stage-4两个层面既保证了现代语义的精确还原又通过esnext与stage/4等入口为不同升级节奏的项目提供了灵活的引入方式。理解其签名、实现与边界行为后你可以在任何受支持的运行环境中放心地把slice().reverse()替换为toReversed()让代码更简洁、语义更明确。【免费下载链接】core-jsStandard Library项目地址: https://gitcode.com/GitHub_Trending/co/core-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考