浏览器最近获得了一种新的可互操作方法,可用于对数组调用:Array.prototype.with()
。
本文介绍了此方法的工作原理,以及如何在不改变原始数组的情况下使用该方法更新数组。
Array.prototype.with(index, value)
简介
Array.prototype.with(index, value)
方法会返回对其调用的数组的副本,并将 index
设置为您提供的新 value
。
以下示例显示了一个年龄数组。您想要创建新的数组副本,同时将第二个年龄从 15 更改为 16:
const ages = [10, 15, 20, 25];
const newAges = ages.with(1, 16);
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)
分解代码:ages.with(...)
在不修改原始数组的情况下返回 ages
变量的副本。ages.with(1, …)
会替换第二项 (index = 1
)。ages.with(1, 16)
会将第二项分配给 16
。
这就是在修改后创建新数组副本的方式。
如果您想确保原始数组保持不变,这会非常有用;此外,本文还介绍了一些相关的使用情形。但现在,我们来看一下如果使用括号表示法,会发生什么情况:
const ages = [10, 15, 20, 25];
const newAges = ages;
newAges[1] = 16;
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 16, 20, 25] (Also changed 🙁)
如您所见,此示例中 ages
变量也经过了修改。这是因为,当您分配 ages = newAges
时,JavaScript 不会复制数组,而是创建对其他数组的引用。因此,两者都指向同一个数组,因此,对其中一项做出的任何更改也会影响另一项。
Array.prototype.with() 和不可变性
不可变性是许多前端库和框架的核心,仅举几例:React(和 redux)、Vue
此外,其他库和框架不一定需要不可变性,但建议使用不可变性以提高性能:Angular 和 Lit
因此,开发者经常不得不使用其他方法返回数组副本,从而影响代码可读性:
const ages = [10, 15, 20, 25];
const newAges = ages.map((age, index) => {
if (index === 1) {
return 16;
}
return age;
});
console.log(newAges); // [10, 16, 20, 25]
console.log(ages); // [10, 15, 20, 25] (Remains unchanged)
以下示例 Codepen 展示了如何在 React 中将 .with()
与 useState 结合使用,以不可变的方式更新项目数组:
由于 .with()
方法会返回数组的副本,因此您可以链接多个 .with()
调用,甚至可以链接其他数组方法。以下示例演示了如何从数组中递增第二个和第三个年龄:
const ages = [10, 15, 20, 25];
const newAges = ages.with(1, ages[1] + 1).with(2, ages[2] + 1)
console.log(newAges); // [10, 16, 21, 25]
console.log(ages); // [10, 15, 20, 25] (unchanged)
其他新的不可变方法
另外三种方法最近变得具有互操作性:
Array.prototype.toReversed()
,用于逆转数组,而不更改原始数组。Array.prototype.toSorted()
,用于对数组进行排序,而不改变原始数组。Array.prototype.toSpliced()
,其工作方式类似于.splice()
,但不会改变原始数组。
根据 MDN,这三种方法为其对应项的复制版本。这些方法还可在需要或不可变的情况下使用。
总而言之,使用本文中介绍的四种方法之一,可以在 JavaScript 中更轻松地实现不可变更新。具体而言,使用 .with()
方法可以更轻松地更新数组的单个元素,而无需更改原始数组。