JavaScript数组遍历数据处理

JavaScript 数组与遍历:map、splice、for of

按是否修改原数组梳理 map、filter、reduce、find、slice、splice、sort、toSorted,以及 for、forEach、for of、for in 的选择方法。

·更新于 ·阅读约 13 分钟·计算中...

选择数组 API 时先回答两个问题:要返回新数组还是修改原数组?是否需要提前退出循环?mapfilterslice 返回新数组;splicesortreverse 会修改原数组;需要 breakcontinueawait 时优先使用 for...of

本文合并旧版数组方法、slice/splice 和循环方式文章。

目录

修改原数组还是返回新数组

返回新数组/值 修改原数组
mapfiltersliceconcat pushpopshiftunshift
toSortedtoReversedtoSpliced sortreversesplice
findsomeeveryreduce fillcopyWithin

React state 和 reducer 中通常优先使用不修改原值的方法,避免引用未变化或旧状态被意外改写。

添加与删除

const tools = ["Raycast", "IINA"]

tools.push("AppCleaner")  // 修改 tools
const last = tools.pop()   // 修改 tools 并返回删除项

需要保持原数组不变:

const withBrew = [...tools, "Homebrew"]
const withoutIINA = tools.filter((tool) => tool !== "IINA")

slice 与 splice

slice(start, end) 返回 [start, end) 范围的新数组,不修改原数组:

const values = [0, 1, 2, 3, 4]
values.slice(1, 4) // [1, 2, 3]
values             // 仍为 [0, 1, 2, 3, 4]

splice(start, deleteCount, ...items) 直接修改原数组,并返回被删除项:

const values = ["a", "b", "c"]
const removed = values.splice(1, 1, "B", "B2")

removed // ["b"]
values  // ["a", "B", "B2", "c"]

现代运行环境还可以用 toSpliced 返回新数组:

const next = values.toSpliced(1, 1, "new")

map、filter 与 reduce

转换每一项使用 map

const names = tools.map((tool) => tool.name)

筛选使用 filter

const freeTools = tools.filter((tool) => tool.price === 0)

聚合为单个结果使用 reduce

const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0)

如果 reduce 回调需要一大段解释,普通循环往往更清楚。不要为了“函数式”把可读逻辑压成一行。

find、some 与 every

const iina = tools.find((tool) => tool.slug === "iina")
const hasFreeTool = tools.some((tool) => tool.price === 0)
const allAvailable = tools.every((tool) => tool.available)
  • find 返回第一个匹配元素或 undefined
  • some 找到一个 true 就停止。
  • every 遇到一个 false 就停止。

只需要判断存在性时不要先 filter 再检查长度。

sort 与 toSorted

sort 会修改原数组,并且默认按字符串比较:

[10, 2, 30].sort() // [10, 2, 30],不是数值升序

数值排序:

const ascending = numbers.toSorted((a, b) => a - b)

如果环境不支持 toSorted

const ascending = [...numbers].sort((a, b) => a - b)

比较函数要保持稳定规则,不要返回随机值。

for、forEach、for...of、for...in

for

需要索引、双指针或精确步长:

for (let index = 0; index < items.length; index += 1) {
  if (items[index].hidden) continue
  render(items[index])
}

forEach

适合对数组每项执行同步副作用,但不能 break,也不会等待 async 回调:

items.forEach((item) => console.log(item.id))

不要这样等待:

items.forEach(async (item) => {
  await save(item)
})

for...of

适合可迭代对象、提前退出和顺序 await:

for (const item of items) {
  if (!item.valid) break
  await save(item)
}

for...in

for...in 遍历对象的可枚举字符串属性名,不推荐用于数组:

for (const key in settings) {
  console.log(key, settings[key])
}

对象自有属性通常可用 Object.keysObject.valuesObject.entries 明确处理。

选择速查

需求 推荐
每项转换 map
保留符合条件的项 filter
查找第一项 find
判断是否存在 some
汇总 reduce 或清晰循环
截取但不修改 slice
原地插入删除 splice
需要 break/continue/顺序 await for...of
需要索引控制 for

对象解构、浅拷贝和深拷贝见 JavaScript 对象复制指南,余数与循环索引见 JavaScript % 运算指南

参考资料

订阅 FreeMac

每周精选:免费 Mac 软件评测、可信来源更新、替代方案和少折腾指南。