Skip to content

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跳过空位,但不会跳过 undefinednull

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: 5

unshift / 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

性能提示

unshiftshift 操作需要移动所有元素,时间复杂度为 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);
// -1

some / 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); // false

ES6+ 新增方法

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); // 1

Array.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', ... }]
// }

性能优化建议

  1. 优先使用不修改原数组的方法mapfilterslice 等,避免副作用
  2. 大数据量避免使用 indexOf 查找:考虑使用 SetMap 优化查找性能
  3. 避免在循环中修改数组长度pushunshift 等操作会触发数组重新分配内存
  4. 使用 for...of 替代 forEach:需要提前终止遍历时更高效
  5. 善用链式调用:但注意多次遍历的性能开销
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);