网站首页 > 博客文章 正文
ECMAScript 2023 规范已尘埃落定。它涵盖了 Array 对象的若干新方法,这些方法有助于使我们的 JS 程序更加可预测和可维护。toSorted/toReversed/toSpliced/with 允许我们在不就地更新的情况下操作数组,而是通过拷贝副本,并修改该副本。
变更与副作用
Array 对象始终存在某些奇葩之处。sort/reverse/splice 等方法会就地更新数组。而 concat/map/filter 等其他方法则会创建数组的副本,然后操作该副本。当我们对对象执行一个就地更新的操作时,这被称为“副作用”(side effects),并且可能会导致系统其他地方出现意外行为。
举个栗子,这是当我们反转数组时的实际情况。
const framework = ['Vue', 'React', 'Angular']
const reversed = framework.reverse()
console.log(reversed)
// => ['Angular', 'React', 'Vue']
console.log(framework)
// => ['Angular', 'React', 'Vue']
console.log(Object.is(framework, reversed))
// => true
如你所见,原数组也被反转了,尽管我们将反转数组的结果赋值给了一个新变量,但这两个变量只是指向了同一个数组。
数组变更和 React
数组变更方法最臭名昭著的问题之一是,当我们在 React 组件中使用它们时。我们无法变更数组,然后尝试将其设置为新状态,因为数组本身是同一个对象,这不会触发新的渲染。相反,我们需要先拷贝数组,然后变更副本,并将其设置为新状态。因此,React 提供了整整一页文档,解释了如何更新数组状态。
先拷贝,再变更
解决此问题的方案是,先拷贝数组,然后变更它。存在若干不同的数组拷贝方案,包括但不限于:
- Array.from()
- ... 展开运算符
- slice() 方法无参调用
const framework = ['Vue', 'React', 'Angular']
const reversed = Array.from(languages).reverse()
console.log(reversed)
// => ['Angular', 'React', 'Vue']
console.log(framework)
// => ['Vue', 'React', 'Angular']
console.log(Object.is(framework, reversed))
// => false
存在解决方案固然很好,但粉丝请记住,首先执行某种不同的拷贝方法并不优雅。
通过拷贝修改的新方法
此乃新方法的用武之地。toSorted/toReversed/toSpliced/with 中的每一个都会为我们拷贝原数组,更改副本,并将其返回。它使得执行这些操作之一都更易编写,因为我们只需要调用一个函数,且更易读。
Array.prototype.toSorted()
toSorted 方法返回一个全新的已排序数组。
const framework = ['Vue', 'React', 'Angular']
const sorted = framework.toSorted()
console.log(sorted)
// => ['Angular', 'React', 'Vue']
console.log(framework)
// => ['Vue', 'React', 'Angular']
sort 方法存在某些意外行为(排序问题),除了拷贝之外,toSorted 也有同样的问题。如果我们要排序特殊字符的数字或字符串,我们仍要小心。确保我们提供的比较器回调函数(比如 String 的 localeCompare)会产生预期的排序结果。
const numbers = [5, 3, 10, 7, 1]
const sorted = numbers.toSorted()
console.log(sorted)
// => [ 1, 10, 3, 5, 7 ]
const sortedCorrectly = numbers.toSorted((a, b) => a - b)
console.log(sortedCorrectly)
// => [ 1, 3, 5, 7, 10 ]
const strings = ['abc', '?bc', 'def']
const sorted = strings.toSorted()
console.log(sorted)
// => [ 'abc', 'def', '?bc' ]
const sortedCorrectly = strings.toSorted((a, b) => a.localeCompare(b))
console.log(sortedCorrectly)
// => [ 'abc', '?bc', 'def' ]
Array.prototype.toReversed()
使用 toReversed 方法会返回一个逆序的新数组。
const framework = ['Vue', 'React', 'Angular']
const reversed = framework.toReversed()
console.log(reversed)
// => ['Angular', 'React', 'Vue']
Array.prototype.toSpliced()
toSpliced 方法与其原版 splice 略有不同。splice 通过在指定的索引处删除和添加元素,就地更改现有数组,并返回包含数组中已删除元素的数组。toSpliced 返回一个新数组,不包含已删除的元素,但包含任何添加的元素。其工作原理如下:
const framework = ['Vue', 'React', 'Angular']
const spliced = framework.toSpliced(2, 1, 'Nuxt', 'Next')
console.log(spliced)
// => ['Vue', 'React', 'Nuxt', 'Next']
如果我们使用 splice 作为其返回值,那么 toSpliced 将不会被替换。如果我们想在不改变原数组的情况下知道被删除的元素,那么我们应该使用 slice() 拷贝方法。
令人头大的是,splice 采用与 slice 不同的参数。splice 采用一个索引以及该索引之后要删除的元素数量,slice 采用两个索引:开始和结束。如果我们想使用 toSpliced 代替 splice,但又想获取被删除的元素,我们可以将 toSpliced 和 slice 应用于原数组,如下所示:
const languages = ['JavaScript', 'TypeScript', 'CoffeeScript']
const startDeletingAt = 2
const deleteCount = 1
const spliced = languages.toSpliced(
startDeletingAt,
deleteCount,
'Dart',
'WebAssembly'
)
const removed = languages.slice(startDeletingAt, startDeletingAt + deleteCount)
console.log(spliced)
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]
console.log(removed)
// => [ 'CoffeeScript' ]
Array.prototype.with()
with 方法相当于使用 [] 方括号表示法更改数组的一个元素的等价方法。因此,不要像这样直接更改数组:
const languages = ['JavaScript', 'TypeScript', 'CoffeeScript']
languages[2] = 'WebAssembly'
console.log(languages)
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]
我们可以拷贝数组并进行更改:
const languages = ['JavaScript', 'TypeScript', 'CoffeeScript']
const updated = languages.with(2, 'WebAssembly')
console.log(updated)
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]
console.log(languages)
// => [ 'JavaScript', 'TypeScript', CoffeeScript' ]
不只是数组
常规数组对象并不是唯一受益于这些新方法的对象。我们还可以在任意 TypedArray 上使用 toSorted/toReversed/with。这就是从 Int8Array 到 BigUint64Array 的所有内容。TypedArray 没有 splice 方法,因此它们没有获得对应的 toSpliced 方法。
粉丝注意事项
我在上文提到 map/filter/concat 等方法已经执行拷贝操作。但这些方法和新的拷贝方法之间存在差异。如果我们继承内置 Array 对象,并在实例上使用 map/flatMap/filter/concat,它会返回相同类型的新实例。如果继承 Array 并使用 toSorted/toReversed/toSpliced/with,结果则会是纯粹的 Array。
class MyArray extends Array {}
const languages = new MyArray('JavaScript', 'TypeScript', 'CoffeeScript')
const upcase = languages.map(language => language.toUpperCase())
console.log(upcase instanceof MyArray)
// => true
const reversed = languages.toReversed()
console.log(reversed instanceof MyArray)
// => false
我们可以使用 MyArray.from 将其转回自定义 Array:
class MyArray extends Array {}
const languages = new MyArray("JavaScript", "TypeScript", "CoffeeScript");
const reversed = MyArray.from(languages.toReversed());
console.log(reversed instance of MyArray);
// => true
兼容性支持
虽然 ECMAScript 2023 规范非常新,但已经对这些新数组方法提供了良好支持。Chrome 110、Safari 16.3、Node.js 20 和 Deno 1.31 都支持这四种方法,并且有适用于尚不支持的平台的 polyfill(功能补丁)和 shim。
猜你喜欢
- 2024-10-27 ES6扩展运算符:详解与实践(es6扩展运算符深拷贝)
- 2024-10-27 Excel|文本花样连接,CONCATENATE、CONCAT、TEXTJOIN帮你完成
- 2024-10-27 Javascript中数组的方法(javascript 数组方法)
- 2024-10-27 LeetCode题集-4 - 寻找两个有序数组的中位数,六种解法,万字讲解
- 2024-10-27 JS中的Array对象——数组的合并、转换、迭代、排序、堆栈
- 2024-10-27 JavaScript 数组方法的介绍(javascript数组方法有哪些)
- 2024-10-27 vue数组更新后不渲染页面与$set的渊源
- 2024-10-27 vue-router的基本使用(vue- router)
- 2024-10-27 ???数组中的逆序对(归并排序思想)
- 2024-10-27 数据分析工具:Pandas架构分析(pandas数据分析模型)
你 发表评论:
欢迎- 367℃用AI Agent治理微服务的复杂性问题|QCon
- 358℃初次使用IntelliJ IDEA新建Maven项目
- 357℃手把手教程「JavaWeb」优雅的SpringMvc+Mybatis整合之路
- 351℃Maven技术方案最全手册(mavena)
- 348℃安利Touch Bar 专属应用,让闲置的Touch Bar活跃起来!
- 346℃InfoQ 2024 年趋势报告:架构篇(infoq+2024+年趋势报告:架构篇分析)
- 345℃IntelliJ IDEA 2018版本和2022版本创建 Maven 项目对比
- 342℃从头搭建 IntelliJ IDEA 环境(intellij idea建包)
- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)