数组是 JavaScript 中最频繁使用的数据结构,掌握数组方法的前端开发效率会大幅提升。根据使用目的,可以将常用数组方法归纳为五类。下面逐个梳理每种方法的核心用途、返回值和注意事项。
一、遍历:逐个访问数组元素
遍历方法的核心是“对每个元素执行一个函数”,但它们的行为和返回值各不相同。
1. forEach —— 最纯粹的遍历
对每个元素执行回调,没有返回值(返回 undefined),无法中途 break 或 return 跳出。
const arr = [1, 2, 3];
arr.forEach((item, index) => {
console.log(`第${index}项:${item}`);
});
// 输出:
// 第0项:1
// 第1项:2
// 第2项:3
实用建议:适用于纯粹的副作用(如打印、修改外部变量)。需要提前终止循环时,使用 for...of 或传统 for 循环。
2. map —— 遍历并映射为新数组
对每个元素执行回调,返回一个新数组,新数组的每一项是回调函数的返回值。不会改变原数组。
const nums = [1, 2, 3];
const doubled = nums.map(n => n * 2); // [2, 4, 6]
常见误区:只遍历无映射需求时用 forEach,不要用 map 而忽略其返回值。
3. filter —— 遍历并过滤
返回一个新数组,只包含使回调返回 true 的元素。
const nums = [1, 2, 3, 4];
const evens = nums.filter(n => n % 2 === 0); // [2, 4]
4. some / every —— 条件测试遍历
some:只要有一个元素通过测试就返回trueevery:所有元素都通过测试才返回true
const arr = [1, 2, 3];
arr.some(n => n > 2); // true
arr.every(n => n > 0); // true
arr.every(n => n > 2); // false
实用建议:some 和 every 存在“短路”机制,一旦结论确定就停止遍历,比遍历所有元素再判断更高效。
二、修改:增删改原数组
这类方法会直接改变原数组,使用时要留意副作用。
1. push / pop —— 末尾添加/删除
const arr = [1, 2];
arr.push(3); // 返回新长度 3,arr 变为 [1,2,3]
arr.pop(); // 返回被删除的元素 3,arr 变回 [1,2]
2. shift / unshift —— 头部删除/添加
const arr = [2, 3];
arr.unshift(1); // 返回新长度 3,arr 变为 [1,2,3]
arr.shift(); // 返回被删除的元素 1,arr 变回 [2,3]
性能警告:shift 和 unshift 会移动所有后续元素,时间复杂度 O(n),大数组慎用。
3. splice —— 任意位置增删改
功能强大,原数组被修改,返回值是删除的元素(若无删除则为空数组)。
const arr = [1, 2, 3, 4];
// 删除从索引1开始的2个元素
const removed = arr.splice(1, 2); // removed = [2,3], arr 变为 [1,4]
// 在索引1处插入元素
arr.splice(1, 0, 5, 6); // arr 变为 [1,5,6,4]
4. fill —— 填充数组
用固定值覆盖数组元素,返回修改后的原数组。
const arr = [1, 2, 3];
arr.fill(0); // [0, 0, 0]
arr.fill(9, 1, 2); // 从索引1到2(不含),填充为9:[0, 9, 0]
三、查询:查找元素或索引
1. indexOf / lastIndexOf —— 查找某值的索引
严格相等比较(===),返回首次匹配索引,找不到返回 -1。
const arr = [1, 2, 3, 2];
arr.indexOf(2); // 1
arr.lastIndexOf(2); // 3
arr.indexOf(4); // -1
2. includes —— 判断是否包含某值
返回 true 或 false,使用严格相等比较。ES7 新增,比 indexOf 语义更清晰。
[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false
3. find / findIndex —— 按条件查找
find:返回第一个让回调返回true的元素本身,找不到返回undefinedfindIndex:返回对应的索引,找不到返回-1
const users = [
{ name: 'Alice', age: 20 },
{ name: 'Bob', age: 25 }
];
users.find(u => u.age > 21); // { name: 'Bob', age: 25 }
users.findIndex(u => u.age > 21); // 1
优势:查找对象数组时远比 indexOf 灵活。
四、转换:生成新数组或聚合为单个值
1. concat —— 合并数组
返回新数组,不改变原数组。
[1, 2].concat([3, 4]); // [1, 2, 3, 4]
// 也可以使用展开运算符:[...[1,2], ...[3,4]]
2. slice —— 截取子数组
返回新数组,包含起止索引之间的元素(不含结束索引)。原数组不变。
const arr = [1, 2, 3, 4];
arr.slice(1, 3); // [2, 3]
arr.slice(1); // [2, 3, 4],省略结束索引则到末尾
3. join —— 数组转字符串
将数组元素用指定分隔符连接成字符串。元素为 undefined/null 时会转为空字符串。
[1, 2, 3].join('-'); // "1-2-3"
['H', 'i'].join(''); // "Hi"
4. reduce / reduceRight —— 聚合为单个值
从左(或右)累积计算,回调接收累加器和当前值。返回值可以是任意类型。
const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, cur) => acc + cur, 0); // 10
强大之处:除了求和,还可以扁平化数组、按条件分组、组合函数等。
5. flat / flatMap —— 数组扁平化
flat(depth):拉平嵌套数组,默认深度 1。flatMap:先map再flat(深度 1),常用于将每个元素映射成一个数组后再合并。
[1, [2, [3]]].flat(2); // [1, 2, 3]
[1, 2, 3].flatMap(n => [n, n*2]); // [1, 2, 2, 4, 3, 6]
五、排序与反转
1. sort —— 排序数组
直接修改原数组,返回排序后的原数组引用。默认按字符串 Unicode 排序,数字排序必须传入比较函数。
const arr = [3, 1, 4, 1, 5];
arr.sort(); // [1, 1, 3, 4, 5](按字符序)
// 数字正确排序
[10, 2, 1].sort((a, b) => a - b); // [1, 2, 10]
[10, 2, 1].sort((a, b) => b - a); // [10, 2, 1](降序)
2. reverse —— 反转顺序
修改原数组,返回原数组引用。
const arr = [1, 2, 3];
arr.reverse(); // [3, 2, 1]
使用建议与避坑指南
| 分类 | 方法 | 是否修改原数组 | 返回值 | 关键注意点 |
|------|------|---------------|--------|------------|
| 遍历 | forEach | 否 | undefined | 无法中途跳出 |
| 遍历 | map | 否 | 新数组 | 新数组长度与原数组一致 |
| 遍历 | filter | 否 | 新数组 | 仅保留条件为 true 的元素 |
| 修改 | push/pop | 是 | 新长度/删除元素 | 未尾操作,O(1) |
| 修改 | shift/unshift | 是 | 新长度/删除元素 | 头部操作,O(n),大数组慎用 |
| 修改 | splice | 是 | 删除的元素数组 | 可增可删可改 |
| 查询 | indexOf/includes | 否 | 索引/布尔 | 严格相等比较 |
| 查询 | find/findIndex | 否 | 元素/索引 | 按条件查找,灵活 |
| 转换 | concat/slice/join | 否 | 新数组/字符串 | 不改变原数组 |
| 转换 | reduce | 否 | 任意值 | 功能极其强大 |
| 排序 | sort/reverse | 是 | 原数组引用 | 直接修改原数组! |
核心原则:需要保留原数组时,先手动拷贝([...arr] 或 arr.slice()),再调用修改类方法。善用 map、filter、reduce 的组合完成复杂数据处理,避免大量副作用代码。