ECMAScript新提案,“Change Array by copy” (新增4个无损数组方法)

237 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第23天,点击查看活动详情

该提案地址为“Change Array by copy”,由Robin Ricard 和 Ashley Claymore提出,目前以进入state3阶段。

TC39规范

首先让我们来了解一下,对于一个提案从提出到最后被纳入ES新特性,在TC39规范中需要分那几步来走呢:

  • stage0(strawman),任何TC39的成员都可以提交。
  • stage1(proposal),进入此阶段就意味着这一提案被认为是正式的了,需要对此提案的场景与API进行详尽的描述。
  • stage2(draft),演进到这一阶段的提案如果能最终进入到标准,那么在之后的阶段都不会有太大的变化,因为理论上只接受增量修改。
  • state3(candidate),这一阶段的提案只有在遇到了重大问题才会修改,规范文档需要被全面的完成。
  • state4(finished),这一阶段的提案将会被纳入到ES每年发布的规范之中。 这意味着该提案在不远的将来即将被浏览器实现支持。

破坏性与非破坏性数组方法

大部分数组方法都是非破坏性的-它们不会改变原数组,如filter方法:

const arr = ['a', 'b', 'b', 'a'];
const result = arr.filter(x => x !== 'b');
console.log(arr)//['a', 'b', 'b', 'a'];
console.log(result)//['a', 'a']

但是也有一下数组方法是破环性,他们改变原数组,如 .sort()

const arr = ['c', 'a', 'b'];
const result = arr.sort();
console.log(arr)//['a', 'b', 'c']
console.log(result)//['a', 'b', 'c']

.sort()方法外,以下两个个方法也具有破环性:

  • .reverse()
  • .splice() 如果我们在使用这些方法,同时又希望不改变原数组,我们只能这样做:
const sorted1 = arr.slice().sort();
const sorted2 = [...arr].sort();
const sorted3 = Array.from(arr).sort();

这些方法的本质都是创建数组的副本,然后再更改这个副本。

新的无损方法

该提案引入了三种Array方法的无损版本:

  • .toReversed(): Array

    无损版本 .reverse()

  • .toSorted(compareFn): Array

    无损版本 .sort()

  • .toSpliced(start, deleteCount, ...items): Array

    无损版本 .splice()

同时该提案还引入了一个无损方法.with(),不过更方法没有对应破环版本。该方法的作用是无损的替换某个值(有点类似于arr[index]=value)

.toReversed(): Array

.toReversed() 是 .reverse()方法的无损版本:

const arr = ['a', 'b', 'c'];
assert.deepEqual(
  arr.toReversed(), ['c', 'b', 'a']
);
assert.deepEqual(
  arr, ['a', 'b', 'c']
);

.toReversed()方法的简单polyfill:

if (!Array.prototype.toReversed) {
  Array.prototype.toReversed = function () {
    return this.slice().reverse();
  };
}

.toSorted(compareFn): Array

.toSorted() 是 .sort()方法的无损版本:

const arr = ['c', 'a', 'b'];
assert.deepEqual(
  arr.toSorted(), ['a', 'b', 'c']
);
assert.deepEqual(
  arr, ['c', 'a', 'b']
);

.toSorted()方法的简单polyfill:

if (!Array.prototype.toSorted) {
  Array.prototype.toSorted = function (compareFn) {
    return this.slice().sort(compareFn);
  };
}

.toSpliced(start, deleteCount, ...items): Array  

.toSpliced() 是 .splice()方法的无损版本。

const arr = ['a', 'b', 'c', 'd'];
assert.deepEqual(
  arr.toSpliced(1, 2, 'X'), [ 'a', 'X', 'd' ]
);
assert.deepEqual(
  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;
  };
}

.with(index, value): Array

arr.with(index, value)arr[index] = value的无损版本。以下代码说明了.with方法的运行过程:

const arr = ['a', 'b', 'c'];
assert.deepEqual(
  arr.with(1, 'X'), ['a', 'X', 'c']
);
assert.deepEqual(
  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;
  };
}

总结

可以预见这几个新增的无损方法以后会提升程序员的编程效率,不过目前浏览器还没有支持,如果要使用,暂时还只能使用polyfill版本。

参考文章

2ality.com/2022/04/cha…