JavaScript 数组方法完全指南
JavaScript 数组方法完全指南
Introduction
JavaScript 数组是现代前端开发中最核心的数据结构之一,而数组方法则是每位开发者必须掌握的利器。从 map、filter、reduce 三大函数式方法,到 find、some、every 等查询方法,再到 push、pop、splice 等变更方法,JavaScript 为我们提供了丰富而强大的数组操作API。掌握这些方法,不仅能让代码更简洁优雅,还能显著提升开发效率。本文将系统梳理ES6以来最常用的数组方法,通过基础语法讲解、代码示例演示、运行效果展示、常见问题解析以及延伸阅读推荐,帮助你建立完整的数组方法知识体系。
一、遍历类方法(Iteration Methods)
1. forEach — 逐项遍历
forEach 是最基础的遍历方法,它对数组中的每个元素执行一次提供的函数,没有返回值。
基础语法:
array.forEach(callback(currentValue[, index[, array]])[, thisArg])
代码示例:
const fruits = ['苹果', '香蕉', '橙子'];
fruits.forEach((item, index, arr) => {
console.log(`索引 ${index}: ${item}`);
});
// 输出:
// 索引 0: 苹果
// 索引 1: 香蕉
// 索引 2: 橙子
运行效果:
索引 0: 苹果
索引 1: 香蕉
索引 2: 橙子
注意事项: forEach 无法使用 break 或 return 跳出循环。如果需要中断遍历,应使用 for...of 循环或 some/every 方法。
---
2. map — 映射转换
map 创建一个新数组,其结果是该数组中的每个元素都调用提供的函数后的返回值。
基础语法:
const newArray = array.map(callback(currentValue[, index[, array]])[, thisArg])
代码示例:
const numbers = [1, 2, 3, 4, 5];
// 求平方
const squared = numbers.map(n => n * n);
console.log('平方数组:', squared);
// 对象属性提取
const users = [
{ name: '张三', age: 25 },
{ name: '李四', age: 30 },
{ name: '王五', age: 28 }
];
const names = users.map(user => user.name);
console.log('用户名列表:', names);
运行效果:
平方数组: [1, 4, 9, 16, 25]
用户名列表: ['张三', '李四', '王五']
注意事项: map 不会改变原数组(除非回调函数内部做了修改),且始终返回一个新数组。如果不需要返回值,应使用 forEach。
---
3. filter — 条件过滤
filter 创建一个包含所有通过测试函数测试的元素的新数组。
基础语法:
const newArray = array.filter(callback(element[, index[, array]])[, thisArg])
代码示例:
const scores = [85, 42, 93, 67, 78, 55, 91];
// 过滤及格分数
const passed = scores.filter(score => score >= 60);
console.log('及格成绩:', passed);
// 过滤偶数
const evens = [1, 2, 3, 4, 5, 6].filter(n => n % 2 === 0);
console.log('偶数:', evens);
// 过滤对象数组
const products = [
{ name: '手机', price: 2999, inStock: true },
{ name: '电脑', price: 5999, inStock: false },
{ name: '耳机', price: 199, inStock: true }
];
const availableProducts = products.filter(p => p.inStock);
console.log('有货商品:', availableProducts.map(p => p.name));
运行效果:
及格成绩: [85, 93, 67, 78, 91]
偶数: [2, 4, 6]
有货商品: ['手机', '耳机']
---
4. reduce — 聚合归纳
reduce 对数组中的每个元素按序执行一个提供的 reducer 函数,将其结果汇总为单个值。
基础语法:
array.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
代码示例:
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((acc, cur) => acc + cur, 0);
console.log('总和:', sum);
// 求积
const product = numbers.reduce((acc, cur) => acc * cur, 1);
console.log('乘积:', product);
// 转换为对象
const fruitCounts = ['苹果', '香蕉', '苹果', '橙子', '香蕉', '苹果'];
const countMap = fruitCounts.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log('水果计数:', countMap);
运行效果:
总和: 15
乘积: 120
水果计数: { '苹果': 3, '香蕉': 2, '橙子': 1 }
---
二、查询类方法(Searching Methods)
5. find / findIndex / findLast
基础语法:
array.find(callback(element[, index[, array]])[, thisArg])
array.findIndex(callback(element[, index[, array]])[, thisArg])
array.findLast(callback(element[, index[, array]])[, thisArg]) // ES2023
代码示例:
const users = [
{ id: 1, name: '张三', role: 'admin' },
{ id: 2, name: '李四', role: 'user' },
{ id: 3, name: '王五', role: 'admin' }
];
// find — 找到第一个匹配元素
const admin = users.find(u => u.role === 'admin');
console.log('第一个管理员:', admin.name);
// findIndex — 找到第一个匹配元素的索引
const adminIndex = users.findIndex(u => u.role === 'admin');
console.log('第一个管理员索引:', adminIndex);
// findLast — 找到最后一个匹配元素(ES2023)
const lastAdmin = users.findLast(u => u.role === 'admin');
console.log('最后一个管理员:', lastAdmin.name);
运行效果:
第一个管理员: 张三
第一个管理员索引: 0
最后一个管理员: 王五
---
6. some / every — 布尔查询
基础语法:
array.some(callback(element[, index[, array]])[, thisArg])
array.every(callback(element[, index[, array]])[, thisArg])
代码示例:
const ages = [18, 22, 15, 30, 25];
// some — 是否有任何元素满足条件
const hasMinor = ages.some(age => age < 18);
console.log('是否有未成年人:', hasMinor);
// every — 是否所有元素都满足条件
const allAdult = ages.every(age => age >= 18);
console.log('是否全部成年:', allAdult);
运行效果:
是否有未成年人: false
是否全部成年: true
---
7. includes / indexOf / lastIndexOf
基础语法:
array.includes(searchElement[, fromIndex])
array.indexOf(searchElement[, fromIndex])
array.lastIndexOf(searchElement[, fromIndex])
代码示例:
const nums = [1, 2, 3, 4, 2, 5, 2];
console.log('包含3:', nums.includes(3)); // true
console.log('首次出现2:', nums.indexOf(2)); // 1
console.log('最后出现2:', nums.lastIndexOf(2)); // 6
运行效果:
包含3: true
首次出现2: 1
最后出现2: 6
---
三、变更类方法(Mutating Methods)
8. push / pop — 末尾操作
基础语法:
array.push(element1[, ...[, elementN]]) // 返回新长度
array.pop() // 返回被移除元素
代码示例:
const stack = [1, 2, 3];
stack.push(4);
console.log('push后:', stack);
const removed = stack.pop();
console.log('pop移除:', removed);
console.log('pop后:', stack);
运行效果:
push后: [1, 2, 3, 4]
pop移除: 4
pop后: [1, 2, 3]
---
9. unshift / shift — 头部操作
基础语法:
array.unshift(element1[, ...[, elementN]]) // 返回新长度
array.shift() // 返回被移除元素
代码示例:
const queue = ['A', 'B', 'C'];
queue.unshift('START');
console.log('unshift后:', queue);
const first = queue.shift();
console.log('shift移除:', first);
console.log('shift后:', queue);
运行效果:
unshift后: ['START', 'A', 'B', 'C']
shift移除: START
shift后: ['A', 'B', 'C']
---
10. splice — 任意位置增删改
splice 是最强大的变更方法,可以从数组中添加或删除元素。
基础语法:
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])
代码示例:
const colors = ['红', '绿', '蓝', '黄', '紫'];
// 删除:移除索引1开始的2个元素
const deleted = colors.splice(1, 2);
console.log('删除的元素:', deleted);
console.log('删除后:', colors);
// 插入:在索引1处插入新元素
colors.splice(1, 0, '粉', '橙');
console.log('插入后:', colors);
// 替换:替换索引2的元素
colors.splice(2, 1, '黑');
console.log('替换后:', colors);
运行效果:
删除的元素: ['绿', '蓝']
删除后: ['红', '黄', '紫']
插入后: ['红', '粉', '橙', '黄', '紫']
替换后: ['红', '粉', '黑', '黄', '紫']
---
四、排序与反转
11. sort — 排序
基础语法:
array.sort([compareFunction(compareFirst, compareSecond)])
代码示例:
const nums = [3, 1, 4, 1, 5, 9, 2, 6];
// 默认字典序(注意:默认会转字符串比较!)
const defaultSort = [...nums].sort();
console.log('默认排序:', defaultSort);
// 数值排序
const numericSort = [...nums].sort((a, b) => a - b);
console.log('升序:', numericSort);
const descendingSort = [...nums].sort((a, b) => b - a);
console.log('降序:', descendingSort);
// 字符串排序
const names = ['Tom', 'Alice', 'Bob', 'Diana'];
console.log('字母排序:', [...names].sort());
运行效果:
默认排序: [1, 2, 3, 4, 5, 6, 9]
升序: [1, 1, 2, 3, 4, 5, 6, 9]
降序: [9, 6, 5, 4, 3, 2, 1, 1]
字母排序: ['Alice', 'Bob', 'Diana', 'Tom']
常见问题: sort() 默认按字符串Unicode码点排序,所以 [1, 2, 10].sort() 会得到 [1, 10, 2]。务必传入比较函数处理数字排序。
---
12. reverse — 反转
基础语法:
array.reverse()
代码示例:
const arr = [1, 2, 3, 4, 5];
const reversed = [...arr].reverse();
console.log('反转后:', reversed);
运行效果:
反转后: [5, 4, 3, 2, 1]
---
五、组合与切片
13. concat — 合并
基础语法:
const newArray = array1.concat(array2[, ...[, arrayN]])
代码示例:
const a = [1, 2];
const b = [3, 4];
const c = [5, 6];
console.log('合并:', a.concat(b, c));
运行效果:
合并: [1, 2, 3, 4, 5, 6]
---
14. slice — 切片
基础语法:
array.slice([begin[, end]])
代码示例:
const letters = ['A', 'B', 'C', 'D', 'E'];
// 提取前3个
console.log('前3个:', letters.slice(0, 3));
// 提取最后2个
console.log('最后2个:', letters.slice(-2));
// 复制整个数组(浅拷贝)
const copy = letters.slice();
console.log('拷贝:', copy);
运行效果:
前3个: ['A', 'B', 'C']
最后2个: ['D', 'E']
拷贝: ['A', 'B', 'C', 'D', 'E']
---
15. flat / flatMap — 扁平化
基础语法:
array.flat([depth]) // depth默认为1
array.flatMap(callback(currentValue[, index[, array]])[, thisArg])
代码示例:
// flat — 扁平化数组
const nested = [1, [2, [3, [4]]]];
console.log('扁平1层:', nested.flat(1));
console.log('完全扁平:', nested.flat(Infinity));
// flatMap — 先map后flat(常用于数据转换)
const sentences = ['Hello world', 'How are you'];
const words = sentences.flatMap(s => s.split(' '));
console.log('单词列表:', words);
运行效果:
扁平1层: [1, 2, [3, [4]]]
完全扁平: [1, 2, 3, 4]
单词列表: ['Hello', 'world', 'How', 'are', 'you']
---
六、其他实用方法
16. join — 拼接为字符串
基础语法:
array.join([separator])
代码示例:
const parts = ['首页', '文章', '列表'];
console.log('默认连接:', parts.join());
console.log('斜杠连接:', parts.join('/'));
console.log('箭头连接:', parts.join(' → '));
运行效果:
默认连接: 首页,文章,列表
斜杠连接: 首页/文章/列表
箭头连接: 首页 → 文章 → 列表
---
17. at — 索引访问(ES2022)
基础语法:
array.at(index) // 支持负索引
代码示例:
const arr = [10, 20, 30, 40, 50];
console.log('第一个:', arr.at(0));
console.log('最后一个:', arr.at(-1));
console.log('倒数第2个:', arr.at(-2));
运行效果:
第一个: 10
最后一个: 50
倒数第2个: 40
---
七、常见问题(FAQ)
Q1:map、filter 和 forEach 的区别是什么?forEach 无返回值,纯粹用于遍历副作用;map 返回新数组(每个元素被转换);filter 返回新数组(只保留满足条件的元素)。
Q2:如何正确对数字数组排序?
// ❌ 错误
[1, 10, 2].sort(); // [1, 10, 2]
// ✅ 正确
[1, 10, 2].sort((a, b) => a - b); // [1, 2, 10]
Q3:reduce 的 initialValue 什么时候必须提供?
当数组可能为空时,建议始终提供 initialValue,否则空数组会抛出 TypeError。
Q4:splice 和 slice 的区别?splice 修改原数组(删除/替换/插入),而 slice 返回新数组(原数组不变)。
Q5:如何深拷贝一个数组?
const deep = JSON.parse(JSON.stringify(arr)); // 简单但有限制
const deep2 = arr.map(item => ({ ...item })); // 浅拷贝对象元素
const deep3 = structuredClone(arr); // ES2023原生深拷贝
Q6:find 和 filter 的区别?find 返回第一个匹配的元素(或 undefined),filter 返回所有匹配元素组成的新数组。
---
八、延伸阅读
1. MDN Web Docs — Array:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array 官方文档,包含完整的方法列表和详细说明。
2. JavaScript Array 进阶:完全攻略:深入讲解函数式编程思想在数组操作中的应用。
3. ES2022 新特性 — Array.at():MDN关于ES2022新增索引方法的介绍。
4. 你不知道的 JavaScript:经典系列书籍,帮助深入理解 JavaScript 底层机制。
5. Lodash 数组方法对比:学习 Lodash 如何对原生数组方法进行补充和增强。