28 个 JavaScript 数组方法:开发者小抄(含现代补充)
Category(分类): JavaScript Status: 已更新
原文整理了 28 个常用数组方法。本文保留原来的 28 项,并补充近年来已在现代运行环境中可用的 findLast、toSorted、toReversed、toSpliced、with 和 Array.fromAsync()。其中前五项属于 ES2023,Array.fromAsync() 属于 ECMAScript 2026。
先记住三个分类:
- 不改变原数组:
map、filter、slice、concat、find、includes、toSorted等; - 改变原数组:
push、pop、shift、unshift、splice、sort、reverse、fill、copyWithin; - 返回什么:有的返回新数组,有的返回元素/索引/布尔值/字符串/数字或迭代器。
数组方法通常是泛型的,会读取 length 和整数索引;但具体方法对稀疏数组空位的处理不同,不要把空位等同于显式的 undefined。
1. map()
对数组中被访问到的每个元素调用回调,返回一个等长的新数组。回调参数依次是元素、索引和原数组:
const numbers = [1, 2, 3, 4]
const doubled = numbers.map((value, index, array) => {
console.log(index, array === numbers)
return value * 2
})
console.log(doubled) // [2, 4, 6, 8]
map() 不会改变原数组,但会保留空位;不要用它只做副作用,如果不需要返回数组应使用 for…of。
2. filter()
返回通过测试函数的元素组成的新数组:
const even = [1, 2, 3, 4, 5].filter(value => value % 2 === 0)
console.log(even) // [2, 4]
回调返回真值才保留元素。原数组不变,结果数组长度可能小于原数组。
3. reduce()
从左到右把多个元素汇总为一个结果:
const total = [1, 2, 3, 4].reduce((sum, value) => sum + value, 0)
console.log(total) // 10
如果不提供初始值,第一项会作为初始累加器;空数组没有初始值时会抛出 TypeError。复杂对象累加时应提供初始值,避免类型和空数组边界问题。
4. reduceRight()
与 reduce() 类似,但从右向左处理:
const text = ['a', 'b', 'c'].reduceRight((result, value) => result + value, '')
console.log(text) // cba
它常用于从右侧组合字符串、解析嵌套结构或实现函数组合。
5. fill()
用同一个值填充 [start, end) 范围,会修改原数组:
const values = [1, 2, 3, 4, 5]
values.fill(0, 1, 4)
console.log(values) // [1, 0, 0, 0, 5]
如果填充的是对象,所有位置会共享同一个对象引用:
const rows = Array(3).fill({ selected: false })
rows[0].selected = true
console.log(rows[1].selected) // true
需要独立对象时使用 Array.from({ length: 3 }, () => ({ selected: false }))。
6. find()
find() 会按索引检查元素,稀疏数组的空位在该回调中可能表现为 undefined;返回第一个通过测试的元素,找不到返回 undefined:
const user = [
{ id: 1, name: 'Ada' },
{ id: 2, name: 'Grace' }
].find(item => item.id === 2)
console.log(user?.name) // Grace
它返回元素本身,不是索引。需要索引时使用 findIndex()。
7. indexOf()
使用严格相等查找第一个索引,找不到返回 -1:
const values = [1, 2, 3, 2]
values.indexOf(2) // 1
values.indexOf(4) // -1
values.indexOf(Number.NaN) // -1
indexOf() 找不到 NaN,需要查找 NaN 时使用 includes()。
8. lastIndexOf()
从后向前返回最后一个匹配元素的索引:
const values = [1, 2, 3, 2]
values.lastIndexOf(2) // 3
values.lastIndexOf(2, 2) // 1
第二个参数是开始搜索的索引,会按规范转换并限制在有效范围内。
9. findIndex()
与 find() 类似,它会按索引检查稀疏数组空位;返回第一个通过测试的索引,找不到返回 -1:
const values = [5, 12, 8, 130, 44]
values.findIndex(value => value > 13) // 3
与 indexOf() 的区别是它接受测试函数,因此可以按对象属性或复杂条件查找。
10. includes()
判断是否包含某个值,使用 SameValueZero:
[1, 2, 3].includes(2) // true
[Number.NaN].includes(Number.NaN) // true
[1, 2, 3].includes(4) // false
第二个参数可指定开始索引:values.includes(2, 2)。
11. pop()
删除并返回最后一个元素,会改变数组长度:
const values = [1, 2, 3]
const last = values.pop()
console.log(last, values) // 3 [1, 2]
空数组调用 pop() 返回 undefined。
12. push()
把一个或多个元素添加到末尾,返回新长度:
const values = [1, 2]
const length = values.push(3, 4)
console.log(length, values) // 4 [1, 2, 3, 4]
数组长度受安全整数和内存限制,不能把 push 当作无限队列。
13. shift()
删除并返回第一个元素,会改变数组长度:
const values = [1, 2, 3]
const first = values.shift()
console.log(first, values) // 1 [2, 3]
对大型数组频繁 shift() 可能需要移动大量索引;队列可以使用头索引或 Deque 风格结构优化。
14. unshift()
在开头添加一个或多个元素,返回新长度并修改原数组:
const values = [2, 3]
values.unshift(0, 1)
console.log(values) // [0, 1, 2, 3]
同样要注意开头插入的移动成本。
15. splice()
从 start 开始删除 deleteCount 个元素,并可插入新元素。它会修改原数组,返回被删除元素数组:
const values = [1, 2, 3, 4]
const removed = values.splice(1, 2, 'a', 'b')
console.log(removed) // [2, 3]
console.log(values) // [1, 'a', 'b', 4]
splice(start) 会删除从 start 到末尾的所有元素;splice(start, 0, value) 可用于插入。
16. slice()
返回 [start, end) 的浅拷贝,不修改原数组:
const values = [1, 2, 3, 4]
const part = values.slice(1, 3)
console.log(part, values) // [2, 3] [1, 2, 3, 4]
复制数组常用 values.slice() 或 [...values],但嵌套对象仍共享引用。
17. join()
把数组元素连接为字符串,默认分隔符是逗号:
[1, 2, 3].join(', ') // '1, 2, 3'
['a', 'b', 'c'].join('') // 'abc'
[1, null, undefined].join('-') // '1--'
null 和 undefined 在 join() 中按空字符串处理。数组嵌套对象会先进行字符串化,不适合作为通用序列化方法。
18. reverse()
原地反转并返回原数组:
const values = [1, 2, 3]
const result = values.reverse()
console.log(values, result === values) // [3, 2, 1] true
需要不改变原数组时使用现代 toReversed() 或 [...values].reverse()。
19. sort()
sort() 原地排序并返回原数组。默认按字符串的 UTF-16 码元顺序排序:
[4, 1, 3, 2, 10].sort() // [1, 10, 2, 3, 4]
const numbers = [4, 1, 3, 2, 10]
numbers.sort((a, b) => a - b)
console.log(numbers) // [1, 2, 3, 4, 10]
ES2019 起规范要求排序稳定,但比较函数仍应保持一致的比较关系。比较函数返回负数表示 a 在前,正数表示 b 在前,零表示顺序相同。
需要保留原数组时使用 toSorted()。
20. some()
只要至少一个元素通过测试就返回 true,会短路:
[1, 3, 5, 8].some(value => value % 2 === 0) // true
空数组调用 some() 返回 false。
21. every()
只有所有元素通过测试才返回 true,会短路:
[2, 4, 6].every(value => value % 2 === 0) // true
[2, 3, 6].every(value => value % 2 === 0) // false
空数组调用 every() 返回 true,这是逻辑上的“全称命题对空集合成立”,使用时要注意业务语义。
22. Array.from()
把可迭代对象或类数组对象转换成新数组,可以传映射函数:
Array.from('abc') // ['a', 'b', 'c']
Array.from(new Set([1, 1, 2])) // [1, 2]
Array.from({ length: 3 }, (_, index) => index + 1) // [1, 2, 3]
它与展开语法不同:Array.from() 可以处理具有 length 的类数组对象,并能直接接收映射回调。
23. Array.of()
无论参数数量和类型如何,都把参数作为数组元素:
Array.of(7) // [7]
Array.of(1, 2, 3) // [1, 2, 3]
Array(7) // 长度为 7 的稀疏数组
Array(1, 2, 3) // [1, 2, 3]
24. Array.isArray()
可靠判断一个值是否为数组,包括跨 realm 场景:
Array.isArray([]) // true
Array.isArray({}) // false
Array.isArray(new Array(3)) // true
不要使用 value instanceof Array 代替它来判断来自其他窗口的数组。
25. at()
按整数索引读取元素,支持负索引,不修改数组:
const values = [10, 20, 30]
values.at(0) // 10
values.at(-1) // 30
values.at(-4) // undefined
它也适用于字符串和 TypedArray 的相应方法。
26. copyWithin()
把数组的一段浅复制到同一个数组的另一个位置,修改元素但不改变长度:
const values = [1, 2, 3, 4, 5]
values.copyWithin(0, 3, 5)
console.log(values) // [4, 5, 3, 4, 5]
参数是 target、start 和 end,区间仍然是左闭右开。
27. flat()
按指定深度展开嵌套数组,返回新数组,默认深度为 1:
[1, 2, [3, 4], [5, [6]]].flat() // [1, 2, 3, 4, 5, [6]]
[1, 2, [3, 4], [5, [6]]].flat(Infinity) // [1, 2, 3, 4, 5, 6]
flat() 会在展开过程中移除被展开层级中的数组空位。它只展开数组,不会把普通对象当作数组展开。
28. flatMap()
先对每个元素映射,再把结果按深度 1 展开,适合一对多转换或过滤:
[1, 2, 3].flatMap(value => [value, value * 10])
// [1, 10, 2, 20, 3, 30]
[1, 2, 3, 4].flatMap(value => value % 2 ? [value] : [])
// [1, 3]
flatMap() 只展开一层,不能用它替代任意深度的 flat(Infinity)。
29. 现代补充:findLast() 与 findLastIndex()
ES2023 从数组末尾开始查找:
const logs = [
{ level: 'info' },
{ level: 'error' },
{ level: 'info' }
]
logs.findLast(log => log.level === 'error') // { level: 'error' }
logs.findLastIndex(log => log.level === 'error') // 1
30. 现代补充:toReversed()
返回反转后的新数组,不修改原数组:
const values = [1, 2, 3]
const reversed = values.toReversed()
console.log(values, reversed) // [1, 2, 3] [3, 2, 1]
31. 现代补充:toSorted()
返回排序后的新数组:
const values = [10, 2, 1]
const sorted = values.toSorted((a, b) => a - b)
console.log(values, sorted) // [10, 2, 1] [1, 2, 10]
32. 现代补充:toSpliced()
返回执行 splice 效果后的新数组,不修改原数组:
const values = [1, 2, 3, 4]
const next = values.toSpliced(1, 2, 'a', 'b')
console.log(values, next) // [1, 2, 3, 4] [1, 'a', 'b', 4]
33. 现代补充:with()
返回把某个索引替换后的新数组,支持负索引:
const values = ['a', 'b', 'c']
const next = values.with(-1, 'z')
console.log(values, next) // ['a', 'b', 'c'] ['a', 'b', 'z']
索引超出范围会抛出 RangeError,与直接访问不存在索引不同。
34. 现代补充:Array.fromAsync()
Array.fromAsync()(ECMAScript 2026)异步收集异步可迭代对象、可迭代对象或类数组对象中的值,并返回 Promise:
async function example() {
const values = await Array.fromAsync([
Promise.resolve(1),
Promise.resolve(2),
3
])
console.log(values) // [1, 2, 3]
}
它适合异步迭代和逐项收集,不能把它理解成一定会并发执行所有异步任务;需要并发控制时应明确使用 Promise.all()、队列或限流器。
三十五、数组方法快速对照表
| 方法 | 返回值 | 是否修改原数组 |
|---|---|---|
map | 新数组 | 否 |
filter | 新数组 | 否 |
reduce / reduceRight | 累加结果 | 否 |
find / findLast | 元素或 undefined | 否 |
findIndex / findLastIndex | 索引或 -1 | 否 |
some / every / includes | 布尔值 | 否 |
slice / flat / flatMap | 新数组 | 否 |
toSorted / toReversed / toSpliced / with | 新数组 | 否 |
from / of / fromAsync | 新数组或 Promise | 不适用 |
push / unshift | 新长度 | 是 |
pop / shift | 删除的元素 | 是 |
splice | 删除的元素数组 | 是 |
sort / reverse | 原数组 | 是 |
fill / copyWithin | 原数组 | 是 |
join | 字符串 | 否 |
使用建议
- 先确认要不要修改原数组;状态管理和并发代码优先使用不可变方法;
- 数值排序显式传入
(a, b) => a - b; reduce()尽量提供正确的初始值;- 查找
NaN使用includes(),查找复杂条件使用find(); - 大数组上的
shift()、unshift()、频繁splice()需要关注移动成本; - 回调中的异步操作不要误以为
map()会等待,使用Promise.all(values.map(...)); Array.fromAsync()、不可变数组方法和迭代器方法要根据目标浏览器/Node 版本做兼容性验证;MDN 的 Baseline 时间不等同于 ECMAScript 标准发布年份。