Appearance
JavaScript 数组核心 API 详解
导航目录
核心概念
JavaScript 数组是有序集合,提供丰富的 API 用于数据的增删改查、遍历转换等操作。掌握这些 API 是前端开发的基础技能。
数组方法速查表
| 方法 | 行为特征 | 返回值 | 是否修改原数组 |
|---|---|---|---|
map | 遍历转换 | 新数组 | ❌ 不影响 |
filter | 条件过滤 | 过滤后的新数组 | ❌ 不影响 |
concat | 数组拼接 | 新合并数组 | ❌ 不影响 |
push / pop | 队尾操作 | 操作后数组长度 / 被删除元素 | ✅ 修改 |
unshift / shift | 队首操作 | 操作后数组长度 / 被删除元素 | ✅ 修改 |
sort | 排序 | 排序后的数组 | ✅ 修改 |
splice | 删除/插入/替换 | 被删除元素数组 | ✅ 修改 |
slice | 区间截取 | 新子数组 | ❌ 不影响 |
join | 数组转字符串 | 连接后的字符串 | ❌ 不影响 |
forEach | 遍历执行 | undefined | ❌ 不影响 |
some | 存在性验证(任一) | boolean | ❌ 不影响 |
every | 全量验证(全部) | boolean | ❌ 不影响 |
indexOf / lastIndexOf | 元素索引查找 | number(找不到返回 -1) | ❌ 不影响 |
includes | 存在性检查 | boolean | ❌ 不影响 |
reduce / reduceRight | 累积计算 | 最终累积值 | ❌ 不影响 |
find / findIndex | 查找元素/索引 | 元素 / 索引(找不到返回 undefined / -1) | ❌ 不影响 |
flat | 数组扁平化 | 扁平化后的新数组 | ❌ 不影响 |
fill | 填充数组 | 修改后的原数组 | ✅ 修改 |
遍历方法详解
map - 映射转换
功能:对数组每个元素执行回调函数,返回新数组
javascript
const numbers = [1, 2, 3, 4];
// 基本用法
const doubled = numbers.map((num) => num * 2);
// [2, 4, 6, 8]
// 带索引参数
const withIndex = numbers.map((num, index) => `${index}: ${num}`);
// ['0: 1', '1: 2', '2: 3', '3: 4']
// 对象数组转换
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
];
const names = users.map((user) => user.name);
// ['Alice', 'Bob']注意
map 会跳过空位,但不会跳过 undefined 和 null。
filter - 条件过滤
功能:返回满足条件的元素组成的新数组
javascript
const numbers = [1, 2, 3, 4, 5, 6];
// 过滤偶数
const evens = numbers.filter((num) => num % 2 === 0);
// [2, 4, 6]
// 过滤对象数组
const users = [
{ name: "Alice", age: 25, active: true },
{ name: "Bob", age: 30, active: false },
{ name: "Charlie", age: 35, active: true },
];
const activeUsers = users.filter((user) => user.active);
// [{ name: 'Alice', age: 25, active: true }, { name: 'Charlie', age: 35, active: true }]reduce - 累积计算
功能:将数组归约为单个值
javascript
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
// 15
// 求最大值
const max = numbers.reduce((acc, curr) => Math.max(acc, curr));
// 5
// 数组转对象(分组统计)
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
const count = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
// { apple: 3, banana: 2, orange: 1 }
// 扁平化数组
const nested = [
[1, 2],
[3, 4],
[5, 6],
];
const flat = nested.reduce((acc, curr) => acc.concat(curr), []);
// [1, 2, 3, 4, 5, 6]forEach - 遍历执行
功能:对每个元素执行回调,无返回值
javascript
const numbers = [1, 2, 3];
numbers.forEach((num, index, array) => {
console.log(`索引 ${index}: ${num}`);
});
// 无法使用 break/continue,如需中断请使用 for...of 或 some/every注意
forEach 不能中断遍历,如需提前终止,请使用 some(返回 true 时停止)或 every(返回 false 时停止)。
修改原数组的方法
push / pop - 队尾操作
javascript
const arr = [1, 2, 3];
// push:在末尾添加元素,返回新长度
const newLength = arr.push(4, 5);
// arr: [1, 2, 3, 4, 5], newLength: 5
// pop:移除末尾元素,返回被移除的元素
const last = arr.pop();
// arr: [1, 2, 3, 4], last: 5unshift / shift - 队首操作
javascript
const arr = [2, 3, 4];
// unshift:在开头添加元素,返回新长度
const newLength = arr.unshift(0, 1);
// arr: [0, 1, 2, 3, 4], newLength: 5
// shift:移除开头元素,返回被移除的元素
const first = arr.shift();
// arr: [1, 2, 3, 4], first: 0性能提示
unshift 和 shift 操作需要移动所有元素,时间复杂度为 O(n),在大数组上性能较差。频繁操作队首建议使用 链表 或 双端队列。
splice - 多功能操作
功能:删除、插入、替换数组元素
javascript
const arr = ["a", "b", "c", "d", "e"];
// 删除:从索引 1 开始删除 2 个元素
const removed = arr.splice(1, 2);
// arr: ['a', 'd', 'e'], removed: ['b', 'c']
// 插入:在索引 1 处插入元素(删除 0 个)
arr.splice(1, 0, "x", "y");
// arr: ['a', 'x', 'y', 'd', 'e']
// 替换:删除 1 个并插入 2 个
arr.splice(2, 1, "z");
// arr: ['a', 'x', 'z', 'd', 'e']sort - 数组排序
javascript
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
// 默认按字符串 Unicode 排序(注意!)
numbers.sort();
// [1, 1, 2, 3, 4, 5, 6, 9] ✓ 数字巧合正确
const mixed = [10, 2, 30];
mixed.sort();
// [10, 2, 30] ✗ 按字符串排序:"10" < "2" < "30"
// 正确做法:提供比较函数
mixed.sort((a, b) => a - b);
// [2, 10, 30] ✓ 升序
mixed.sort((a, b) => b - a);
// [30, 10, 2] ✓ 降序
// 对象数组排序
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 20 },
];
users.sort((a, b) => a.age - b.age);
// 按年龄升序排列注意
sort 会修改原数组!如需保留原数组,先使用 slice() 复制:
javascript
const sorted = arr.slice().sort((a, b) => a - b);不修改原数组的方法
slice - 截取子数组
javascript
const arr = [1, 2, 3, 4, 5];
// 从索引 1 开始到索引 3(不包括)
const sub1 = arr.slice(1, 3);
// [2, 3]
// 从索引 2 开始到末尾
const sub2 = arr.slice(2);
// [3, 4, 5]
// 负数索引:从末尾开始
const sub3 = arr.slice(-2);
// [4, 5]
// 复制整个数组(浅拷贝)
const copy = arr.slice();
// [1, 2, 3, 4, 5]concat - 合并数组
javascript
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [5, 6];
// 合并多个数组
const combined = arr1.concat(arr2, arr3);
// [1, 2, 3, 4, 5, 6]
// 合并数组和值
const mixed = arr1.concat(3, [4, 5], 6);
// [1, 2, 3, 4, 5, 6]
// ES6 替代方案:展开运算符
const combinedES6 = [...arr1, ...arr2, ...arr3];join - 数组转字符串
javascript
const arr = ["Hello", "World", "JavaScript"];
// 默认用逗号连接
const str1 = arr.join();
// 'Hello,World,JavaScript'
// 指定分隔符
const str2 = arr.join(" ");
// 'Hello World JavaScript'
const str3 = arr.join("-");
// 'Hello-World-JavaScript'
// 与 split 配合使用
const original = str3.split("-");
// ['Hello', 'World', 'JavaScript']查找方法对比
indexOf / lastIndexOf
javascript
const arr = [1, 2, 3, 2, 1];
// 从前向后查找
arr.indexOf(2); // 1
arr.indexOf(2, 2); // 3(从索引 2 开始查找)
arr.indexOf(5); // -1(不存在)
// 从后向前查找
arr.lastIndexOf(2); // 3
arr.lastIndexOf(2, 2); // 1(从索引 2 向前查找)局限性
indexOf 使用 严格相等(===) 比较,无法查找 NaN:
javascript
[1, 2, NaN].indexOf(NaN); // -1 ✗includes
javascript
const arr = [1, 2, 3, NaN];
arr.includes(2); // true
arr.includes(5); // false
arr.includes(NaN); // true ✓ 可以识别 NaN
// 指定起始索引
arr.includes(2, 2); // false(从索引 2 开始查找)find / findIndex
javascript
const users = [
{ id: 1, name: "Alice", age: 25 },
{ id: 2, name: "Bob", age: 30 },
{ id: 3, name: "Charlie", age: 35 },
];
// 查找第一个满足条件的元素
const user = users.find((u) => u.age > 25);
// { id: 2, name: 'Bob', age: 30 }
// 查找第一个满足条件的索引
const index = users.findIndex((u) => u.age > 25);
// 1
// 找不到时
const notFound = users.find((u) => u.age > 50);
// undefined
const notFoundIndex = users.findIndex((u) => u.age > 50);
// -1some / every
javascript
const numbers = [1, 2, 3, 4, 5];
// some:是否有任一元素满足条件
numbers.some((num) => num > 4); // true(5 > 4)
numbers.some((num) => num > 10); // false
// every:是否所有元素都满足条件
numbers.every((num) => num > 0); // true
numbers.every((num) => num > 3); // false
// 实际应用:检查数组是否包含某类元素
const users = [
{ name: "Alice", role: "admin" },
{ name: "Bob", role: "user" },
];
const hasAdmin = users.some((u) => u.role === "admin"); // true
const allActive = users.every((u) => u.active === true); // falseES6+ 新增方法
flat / flatMap - 数组扁平化
javascript
const nested = [1, [2, 3], [4, [5, 6]]];
// flat:默认扁平化一层
nested.flat();
// [1, 2, 3, 4, [5, 6]]
// 指定深度
nested.flat(2);
// [1, 2, 3, 4, 5, 6]
// 完全扁平化
nested.flat(Infinity);
// [1, 2, 3, 4, 5, 6]
// flatMap:先 map 再 flat(1)
const sentences = ["Hello world", "Good morning"];
const words = sentences.flatMap((s) => s.split(" "));
// ['Hello', 'world', 'Good', 'morning']at - 支持负索引访问
javascript
const arr = [1, 2, 3, 4, 5];
// 传统方式获取最后一个元素
arr[arr.length - 1]; // 5
// ES2022 at 方法
arr.at(-1); // 5
arr.at(-2); // 4
arr.at(0); // 1Array.from - 类数组转数组
javascript
// 将类数组对象转为真数组
const arrayLike = { 0: "a", 1: "b", length: 2 };
const arr = Array.from(arrayLike);
// ['a', 'b']
// 将 Set/Map 转为数组
const set = new Set([1, 2, 3]);
Array.from(set); // [1, 2, 3]
// 配合 map 使用
Array.from([1, 2, 3], (x) => x * 2);
// [2, 4, 6]
// 生成指定长度的数组
Array.from({ length: 5 }, (_, i) => i);
// [0, 1, 2, 3, 4]常见陷阱与最佳实践
1. 稀疏数组问题
javascript
// 创建稀疏数组(包含空位)
const sparse = new Array(3); // [empty × 3]
const sparse2 = [1, , 3]; // [1, empty, 3]
// 遍历行为差异
sparse.map((_, i) => i); // [empty × 3](跳过空位)
sparse.forEach((_, i) => i); // 不执行(跳过空位)
// 解决方案:使用 Array.from 填充
const filled = Array.from({ length: 3 }, (_, i) => i);
// [0, 1, 2]
// 或使用 fill
const filled2 = new Array(3).fill(0);
// [0, 0, 0]2. 修改原数组的坑
javascript
// 错误:在遍历时修改数组
const arr = [1, 2, 3, 4, 5];
arr.forEach((num, index) => {
if (num % 2 === 0) {
arr.splice(index, 1); // ✗ 会导致索引错乱
}
});
// 正确:先过滤再处理
const evens = arr.filter((num) => num % 2 === 0);
// 或创建新数组
const result = arr.filter((num) => num % 2 !== 0);3. 类数组对象操作
javascript
// 类数组对象可以使用数组方法
function sum() {
// arguments 是类数组对象
return Array.from(arguments).reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4); // 10
// 更推荐:使用剩余参数
function sumBetter(...numbers) {
return numbers.reduce((a, b) => a + b, 0);
}4. 数组去重
javascript
// 方法 1:使用 Set(推荐)
const unique = [...new Set([1, 2, 2, 3, 3, 3])];
// [1, 2, 3]
// 方法 2:使用 filter
const unique2 = [1, 2, 2, 3, 3, 3].filter(
(item, index, arr) => arr.indexOf(item) === index
);
// 方法 3:使用 reduce
const unique3 = [1, 2, 2, 3, 3, 3].reduce(
(acc, curr) => (acc.includes(curr) ? acc : [...acc, curr]),
[]
);5. 数组分组
javascript
const people = [
{ name: "Alice", age: 21, group: "A" },
{ name: "Bob", age: 25, group: "B" },
{ name: "Charlie", age: 21, group: "A" },
];
// 按年龄分组
const groupedByAge = people.reduce((acc, person) => {
const key = person.age;
if (!acc[key]) acc[key] = [];
acc[key].push(person);
return acc;
}, {});
// {
// 21: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
// 25: [{ name: 'Bob', ... }]
// }性能优化建议
- 优先使用不修改原数组的方法:
map、filter、slice等,避免副作用 - 大数据量避免使用
indexOf查找:考虑使用Set或Map优化查找性能 - 避免在循环中修改数组长度:
push、unshift等操作会触发数组重新分配内存 - 使用
for...of替代forEach:需要提前终止遍历时更高效 - 善用链式调用:但注意多次遍历的性能开销
javascript
// 链式调用示例(清晰但多次遍历)
const result = arr
.filter((x) => x > 0)
.map((x) => x * 2)
.reduce((a, b) => a + b, 0);
// 性能优化(单次遍历)
const result = arr.reduce((acc, x) => (x > 0 ? acc + x * 2 : acc), 0);