ECMAScript 提案:toReversed()、toSorted()、toSpliced()、with()
今天来看一个实用的 ECMAScript 提案:通过副本更改数组。目前该提案处于第 3 阶段。该提案为数组和类型化数组提出了四种新的方法:
.toReversed()
.toSorted()
.toSpliced()
.with()
1. 概述
为什么会有这个提案呢?我们知道,大多数的数组方法都是非破坏性的,也就是说,在数组执行该方法时,不会改变原数组,比如 filter()
方法:
const arr = ['a', 'b', 'b', 'a'];
const result = arr.filter(x => x !== 'b');
console.log(result); // ['a', 'a']
当然,也有一些是破坏性的方法,它们在执行时会改变原数组,比如 sort()
方法:
const arr = ['c', 'a', 'b'];
const result = arr.sort();
console.log(result); // ['a', 'b', 'c']
下面这些数组方法是具有破坏性的:
.reverse()
.sort()
.splice()
如果我们想要将这些数组方法应用于数组而不改变它,可以使用下面任意一种形式:
const sorted1 = arr.slice().sort();
const sorted2 = [...arr].sort();
const sorted3 = Array.from(arr).sort();
可以看到,我们首先需要创建数组的副本,再对这个副本进行修改。
因此该提案就引入了这三个方法的非破坏性版本,因此不需要手动创建副本再进行操作:
.reverse()
的非破坏性版本:.toReversed()
.sort()
非破坏性版本:.toSorted(compareFn)
.splice()
非破坏性版本:.toSpliced(start, deleteCount, ...items)
该提案将这些函数属性引入到 Array.prototype
:
Array.prototype.toReversed() -> Array Array.prototype.toSorted(compareFn) -> Array Array.prototype.toSpliced(start, deleteCount, ...items) -> Array Array.prototype.with(index, value) -> Array
除此之外,该提案还还提出了一个新的非破坏性方法:with()
。该方法会以非破坏性的方式替换给定 index 处的数组元素,即 arr[index]=value
的非破坏性版本。
所有这些方法都将保持目标数组不变,并返回它的副本并执行更改。这些方法适用于数组,也适用于类型化数组,即以下类的实例:
Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array BigInt64Array BigUint64Array
TypedArray是一种通用的固定长度缓冲区类型,允许读取缓冲区中的二进制数据。其在WEBGL规范中被引入用于解决Javascript处理二进制数据的问题。类型化数组也是数组,只不过其元素被设置为特定类型的值。
类型化数组的核心就是一个名为 ArrayBuffer 的类型。每个ArrayBuffer对象表示的只是内存中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过ArrayBuffer能做的就是为了将来使用而分配一定数量的字节。
这些提案也适用于元组,元组相当于不可变的数组。它们拥有数组的所有方法——除了破坏性的方法。因此,将后者的非破坏性版本添加到数组对元组是有帮助的,这意味着我们可以使用相同的方法来非破坏性地更改数组和元组。
2. toReversed()
.toReversed()
是 .reverse()
方法的非破坏性版本:
const arr = ['a', 'b', 'c'];
const result = arr.toReversed();
console.log(result); // ['c', 'b', 'a']
console.log(arr); // ['a', 'b', 'c']
下面是 .toReversed()
方法的一个简单的 polyfill:
if (!Array.prototype.toReversed) {
Array.prototype.toReversed = function () {
return this.slice().reverse();
};
}
3. toSorted()
.toSorted()
是 .sort()
方法的非破坏性版本:
const arr = ['c', 'a', 'b'];
const result = arr.toSorted();
console.log(result); // ['a', 'b', 'c']
console.log(arr); // ['c', 'a', 'b']
下面是 .toSorted()
方法的一个简单的 polyfill:
if (!Array.prototype.toSorted) {
Array.prototype.toSorted = function (compareFn) {
return this.slice().sort(compareFn);
};
}
4. toSpliced()
.splice()
方法比其他几种方法都复杂,其使用形式:splice(start, deleteCount, ...items)
。该方法会从从 start
索引处开始删除 deleteCount
个元素,然后在 start
索引处开始插入item
中的元素,最后返回已经删除的元素。
.toSpliced
是 .splice()
方法的非破坏性版本,它会返回更新后的数组,原数组不会变化,并且我们无法再得到已经删除的元素:
const arr = ['a', 'b', 'c', 'd'];
const result = arr.toSpliced(1, 2, 'X');
console.log(result); // ['a', 'X', 'd']
console.log(arr); // ['a', 'b', 'c', 'd']
下面是 .toSpliced()
方法的一个简单的 polyfill:
if (!Array.prototype.toSpliced) {
Array.prototype.toSpliced = function (start, deleteCount, ...items) {
const copy = this.slice();
copy.splice(start, deleteCount, ...items);
return copy;
};
}
5. with()
.with()
方法的使用形式:.with(index, value)
,它是 arr[index] = value
的非破坏性版本:
const arr = ['a', 'b', 'c'];
const result = arr.with(1, 'X');
console.log(result); // ['a', 'X', 'c']
console.log(arr); // ['a', 'b', 'c']
下面是 .with()
方法的一个简单的 polyfill:
if (!Array.prototype.with) {
Array.prototype.with = function (index, value) {
const copy = this.slice();
copy[index] = value;
return copy;
};
}
提案:https://github.com/tc39/proposal-change-array-by-copy
参考:https://2ality.com/2022/04/change-array-by-copy.html
往期推荐: