查看原文
其他

ECMAScript 提案:toReversed()、toSorted()、toSpliced()、with()

CUGGZ 前端充电宝 2022-07-21

今天来看一个实用的 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(12'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

往期推荐:

不可错过的实用前端工具

你不知道的 CSS 父选择器

React 中的浅比较是如何工作的?

SASS 用法指南

玩转 CSS 变量

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存