JavaScript 数组方法完全指南
前言
数组是 JavaScript 中最常用的数据结构之一,它提供了丰富的内置方法来处理数据。掌握这些方法不仅能提高开发效率,还能让代码更加简洁优雅。本文将全面介绍 JavaScript 数组的各种方法,帮助你从入门到精通。
目录
数组基础
在 JavaScript 中,数组是一种特殊的对象,用于存储有序的数据集合。数组具有以下特点:
- 可调整大小:数组长度可以动态变化
- 可包含不同类型:同一个数组可以存储数字、字符串、对象等
- 索引从 0 开始:第一个元素的索引是 0
- 浅拷贝:数组复制操作创建的是浅拷贝
javascript
// 数组示例
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits[0]); // 'apple'
console.log(fruits.length); // 3
创建数组
1. 字面量方式
javascript
const arr1 = []; // 空数组
const arr2 = [1, 2, 3]; // 包含元素的数组
const arr3 = ['a', 'b', 'c', 1, 2, true]; // 混合类型
2. Array 构造函数
javascript
const arr1 = new Array(); // 空数组
const arr2 = new Array(5); // 长度为 5 的稀疏数组
const arr3 = new Array(1, 2, 3); // [1, 2, 3]
3. Array.of()
javascript
const arr1 = Array.of(7); // [7]
const arr2 = Array.of(1, 2, 3); // [1, 2, 3]
// 与 new Array(7) 不同,Array.of(7) 创建的是包含一个元素 7 的数组
4. Array.from()
javascript
// 从类数组对象创建
const arr1 = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
const arr2 = Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
// 从 Set/Map 创建
const set = new Set([1, 2, 3]);
const arr3 = Array.from(set); // [1, 2, 3]
数组方法分类
增删改查方法
push() - 在末尾添加元素
javascript
const fruits = ['apple', 'banana'];
fruits.push('orange'); // 返回新长度 3
console.log(fruits); // ['apple', 'banana', 'orange']
// 可以一次添加多个元素
fruits.push('grape', 'mango');
console.log(fruits); // ['apple', 'banana', 'orange', 'grape', 'mango']
特点:
- 修改原数组
- 返回数组的新长度
- 可以添加多个元素
pop() - 移除并返回最后一个元素
javascript
const fruits = ['apple', 'banana', 'orange'];
const last = fruits.pop(); // 'orange'
console.log(fruits); // ['apple', 'banana']
console.log(last); // 'orange'
特点:
- 修改原数组
- 返回被移除的元素
- 如果数组为空,返回
undefined
unshift() - 在开头添加元素
javascript
const fruits = ['banana', 'orange'];
fruits.unshift('apple'); // 返回新长度 3
console.log(fruits); // ['apple', 'banana', 'orange']
特点:
- 修改原数组
- 返回数组的新长度
- 性能比
push()低(需要移动所有元素)
shift() - 移除并返回第一个元素
javascript
const fruits = ['apple', 'banana', 'orange'];
const first = fruits.shift(); // 'apple'
console.log(fruits); // ['banana', 'orange']
特点:
- 修改原数组
- 返回被移除的元素
- 性能比
pop()低
splice() - 删除、插入或替换元素
javascript
const fruits = ['apple', 'banana', 'orange', 'grape'];
// 删除元素:从索引 1 开始删除 2 个元素
const removed = fruits.splice(1, 2);
console.log(fruits); // ['apple', 'grape']
console.log(removed); // ['banana', 'orange']
// 插入元素:从索引 1 开始,删除 0 个,插入新元素
fruits.splice(1, 0, 'mango', 'peach');
console.log(fruits); // ['apple', 'mango', 'peach', 'grape']
// 替换元素:从索引 1 开始,删除 1 个,插入新元素
fruits.splice(1, 1, 'banana');
console.log(fruits); // ['apple', 'banana', 'peach', 'grape']
语法 :array.splice(start, deleteCount, ...items)
特点:
- 修改原数组
- 返回被删除元素的数组
- 功能强大,可以删除、插入、替换
slice() - 提取数组片段(不修改原数组)
javascript
const fruits = ['apple', 'banana', 'orange', 'grape', 'mango'];
// 提取从索引 1 到 3(不包括 3)的元素
const sliced = fruits.slice(1, 3);
console.log(sliced); // ['banana', 'orange']
console.log(fruits); // 原数组不变
// 只提供开始索引,提取到末尾
const fromIndex2 = fruits.slice(2);
console.log(fromIndex2); // ['orange', 'grape', 'mango']
// 负数索引:从末尾开始计算
const lastTwo = fruits.slice(-2);
console.log(lastTwo); // ['grape', 'mango']
// 复制整个数组
const copy = fruits.slice();
特点:
- 不修改原数组
- 返回新数组
- 常用于数组复制
遍历方法
forEach() - 遍历数组
javascript
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((value, index, array) => {
console.log(`索引 ${index}: 值 ${value}`);
});
// 输出:
// 索引 0: 值 1
// 索引 1: 值 2
// 索引 2: 值 3
// 索引 3: 值 4
// 索引 4: 值 5
// 修改原数组(不推荐,但可以)
const doubled = [];
numbers.forEach(num => {
doubled.push(num * 2);
});
console.log(doubled); // [2, 4, 6, 8, 10]
特点:
- 不返回新数组(返回
undefined) - 无法使用
break或continue(可以使用return跳过当前迭代) - 不会跳过空槽(稀疏数组)
map() - 映射数组(返回新数组)
javascript
const numbers = [1, 2, 3, 4, 5];
// 将每个数字乘以 2
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // 原数组不变 [1, 2, 3, 4, 5]
// 提取对象属性
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 35 }
];
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob', 'Charlie']
特点:
- 不修改原数组
- 返回新数组
- 新数组长度与原数组相同
- 最常用的数组方法之一
filter() - 过滤数组
javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 筛选偶数
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// 筛选对象
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 }
];
const adults = users.filter(user => user.age >= 18);
console.log(adults); // [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }]
特点:
- 不修改原数组
- 返回新数组
- 新数组长度可能小于原数组
- 回调函数返回
true的元素会被保留
reduce() - 归约数组
javascript
const numbers = [1, 2, 3, 4, 5];
// 求和
const sum = numbers.reduce((accumulator, currentValue) => {
return accumulator + currentValue;
}, 0);
console.log(sum); // 15
// 简化写法
const sum2 = numbers.reduce((acc, cur) => acc + cur, 0);
// 求最大值
const max = numbers.reduce((acc, cur) => Math.max(acc, cur));
// 数组转对象
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user.name;
return acc;
}, {});
console.log(userMap); // { 1: 'Alice', 2: 'Bob', 3: 'Charlie' }
// 数组扁平化
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, cur) => acc.concat(cur), []);
console.log(flat); // [1, 2, 3, 4, 5, 6]
特点:
- 不修改原数组
- 返回单个值
- 功能强大,可以实现很多复杂操作
- 初始值可选(建议总是提供)
reduceRight() - 从右到左归约
javascript
const numbers = [1, 2, 3, 4];
// 从右到左计算
const result = numbers.reduceRight((acc, cur) => acc - cur);
console.log(result); // -2 (4 - 3 - 2 - 1 = -2)
// 与 reduce 对比
const result2 = numbers.reduce((acc, cur) => acc - cur);
console.log(result2); // -8 (1 - 2 - 3 - 4 = -8)
查找方法
indexOf() - 查找元素索引
javascript
const fruits = ['apple', 'banana', 'orange', 'banana'];
const index = fruits.indexOf('banana');
console.log(index); // 1
// 从指定位置开始查找
const index2 = fruits.indexOf('banana', 2);
console.log(index2); // 3
// 找不到返回 -1
const index3 = fruits.indexOf('grape');
console.log(index3); // -1
特点:
- 使用严格相等(===)比较
- 返回第一个匹配的索引
- 找不到返回 -1
lastIndexOf() - 从后往前查找
javascript
const fruits = ['apple', 'banana', 'orange', 'banana'];
const index = fruits.lastIndexOf('banana');
console.log(index); // 3
includes() - 判断是否包含元素
javascript
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.includes('banana')); // true
console.log(fruits.includes('grape')); // false
// 从指定位置开始查找
console.log(fruits.includes('apple', 1)); // false
特点:
- 返回布尔值
- ES6 新增方法
- 比
indexOf() !== -1更语义化
find() - 查找第一个满足条件的元素
javascript
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 17 },
{ id: 3, name: 'Charlie', age: 30 }
];
// 查找第一个年龄大于等于 18 的用户
const adult = users.find(user => user.age >= 18);
console.log(adult); // { id: 1, name: 'Alice', age: 25 }
// 找不到返回 undefined
const senior = users.find(user => user.age > 100);
console.log(senior); // undefined
特点:
- 不修改原数组
- 返回第一个匹配的元素
- 找不到返回
undefined
findIndex() - 查找第一个满足条件的元素索引
javascript
const users = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 17 },
{ id: 3, name: 'Charlie', age: 30 }
];
const index = users.findIndex(user => user.age >= 18);
console.log(index); // 0
// 找不到返回 -1
const notFound = users.findIndex(user => user.age > 100);
console.log(notFound); // -1
findLast() - 查找最后一个满足条件的元素(ES2023)
javascript
const numbers = [1, 2, 3, 4, 5, 6];
const lastEven = numbers.findLast(num => num % 2 === 0);
console.log(lastEven); // 6
findLastIndex() - 查找最后一个满足条件的元素索引(ES2023)
javascript
const numbers = [1, 2, 3, 4, 5, 6];
const lastEvenIndex = numbers.findLastIndex(num => num % 2 === 0);
console.log(lastEvenIndex); // 5
转换方法
join() - 数组转字符串
javascript
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.join()); // 'apple,banana,orange'
console.log(fruits.join('')); // 'applebananaorange'
console.log(fruits.join(' - ')); // 'apple - banana - orange'
特点:
- 不修改原数组
- 默认使用逗号分隔
- 空数组返回空字符串
toString() - 转换为字符串
javascript
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.toString()); // 'apple,banana,orange'
// 等同于 fruits.join()
toLocaleString() - 本地化字符串
javascript
const numbers = [1234.56, 7890.12];
console.log(numbers.toLocaleString('zh-CN')); // '1,234.56,7,890.12'
flat() - 扁平化数组
javascript
const nested = [1, [2, 3], [4, [5, 6]]];
// 默认只扁平化一层
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
// 指定深度
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]
// 扁平化所有层级
console.log(nested.flat(Infinity)); // [1, 2, 3, 4, 5, 6]
特点:
- 不修改原数组
- ES2019 新增
- 可以指定扁平化深度
flatMap() - 映射后扁平化
javascript
const numbers = [1, 2, 3];
// 相当于 map().flat()
const result = numbers.flatMap(x => [x, x * 2]);
console.log(result); // [1, 2, 2, 4, 3, 6]
// 与 map().flat() 对比
const result2 = numbers.map(x => [x, x * 2]).flat();
console.log(result2); // [1, 2, 2, 4, 3, 6]
特点:
- 不修改原数组
- ES2019 新增
- 性能比
map().flat()更好
排序和反转
sort() - 排序
javascript
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
// 默认按字符串排序(不推荐)
numbers.sort();
console.log(numbers); // [1, 1, 2, 3, 4, 5, 6, 9]
// 数字排序(升序)
const numbers2 = [3, 1, 4, 1, 5, 9, 2, 6];
numbers2.sort((a, b) => a - b);
console.log(numbers2); // [1, 1, 2, 3, 4, 5, 6, 9]
// 数字排序(降序)
const numbers3 = [3, 1, 4, 1, 5, 9, 2, 6];
numbers3.sort((a, b) => b - a);
console.log(numbers3); // [9, 6, 5, 4, 3, 2, 1, 1]
// 对象排序
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 17 },
{ name: 'Charlie', age: 30 }
];
users.sort((a, b) => a.age - b.age);
console.log(users);
// [{ name: 'Bob', age: 17 }, { name: 'Alice', age: 25 }, { name: 'Charlie', age: 30 }]
特点:
- 修改原数组
- 默认按字符串 Unicode 码点排序
- 需要提供比较函数进行数字排序
reverse() - 反转数组
javascript
const fruits = ['apple', 'banana', 'orange'];
fruits.reverse();
console.log(fruits); // ['orange', 'banana', 'apple']
特点:
- 修改原数组
- 返回反转后的数组(原数组的引用)
toSorted() - 排序(不修改原数组,ES2023)
javascript
const numbers = [3, 1, 4, 1, 5];
const sorted = numbers.toSorted((a, b) => a - b);
console.log(sorted); // [1, 1, 3, 4, 5]
console.log(numbers); // [3, 1, 4, 1, 5] 原数组不变
toReversed() - 反转(不修改原数组,ES2023)
javascript
const fruits = ['apple', 'banana', 'orange'];
const reversed = fruits.toReversed();
console.log(reversed); // ['orange', 'banana', 'apple']
console.log(fruits); // ['apple', 'banana', 'orange'] 原数组不变
判断方法
every() - 判断是否所有元素都满足条件
javascript
const numbers = [2, 4, 6, 8, 10];
// 判断是否都是偶数
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven); // true
const numbers2 = [2, 4, 6, 7, 8];
const allEven2 = numbers2.every(num => num % 2 === 0);
console.log(allEven2); // false
特点:
- 不修改原数组
- 返回布尔值
- 空数组返回
true(空真值)
some() - 判断是否有元素满足条件
javascript
const numbers = [1, 3, 5, 7, 8];
// 判断是否有偶数
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
const numbers2 = [1, 3, 5, 7, 9];
const hasEven2 = numbers2.some(num => num % 2 === 0);
console.log(hasEven2); // false
特点:
- 不修改原数组
- 返回布尔值
- 空数组返回
false
归约方法
reduce() 和 reduceRight()
已在前面详细介绍,这里不再重复。
ES6+ 新方法
Array.from() - 从类数组创建数组
javascript
// 从字符串
Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
// 从 Set
Array.from(new Set([1, 2, 3])); // [1, 2, 3]
// 从对象(需要 length 属性)
Array.from({ length: 5 }, (_, i) => i); // [0, 1, 2, 3, 4]
// 从 NodeList
const divs = document.querySelectorAll('div');
const divArray = Array.from(divs);
Array.of() - 创建数组
javascript
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
// 与 new Array(7) 不同
fill() - 填充数组
javascript
// 填充整个数组
const arr1 = new Array(5).fill(0);
console.log(arr1); // [0, 0, 0, 0, 0]
// 从指定位置填充
const arr2 = [1, 2, 3, 4, 5];
arr2.fill(0, 2, 4);
console.log(arr2); // [1, 2, 0, 0, 5]
特点:
- 修改原数组
- 可以指定填充的起始和结束位置
copyWithin() - 复制数组元素
javascript
const arr = [1, 2, 3, 4, 5];
// 将索引 0-2 的元素复制到索引 3 开始的位置
arr.copyWithin(3, 0, 3);
console.log(arr); // [1, 2, 3, 1, 2]
特点:
- 修改原数组
- 使用场景较少
entries() - 返回键值对迭代器
javascript
const fruits = ['apple', 'banana', 'orange'];
for (const [index, value] of fruits.entries()) {
console.log(index, value);
}
// 0 'apple'
// 1 'banana'
// 2 'orange'
keys() - 返回键迭代器
javascript
const fruits = ['apple', 'banana', 'orange'];
for (const index of fruits.keys()) {
console.log(index);
}
// 0
// 1
// 2
values() - 返回值迭代器
javascript
const fruits = ['apple', 'banana', 'orange'];
for (const value of fruits.values()) {
console.log(value);
}
// 'apple'
// 'banana'
// 'orange'
at() - 按索引访问(支持负数,ES2022)
javascript
const fruits = ['apple', 'banana', 'orange'];
console.log(fruits.at(0)); // 'apple'
console.log(fruits.at(-1)); // 'orange'(最后一个)
console.log(fruits.at(-2)); // 'banana'(倒数第二个)
特点:
- 不修改原数组
- 支持负数索引
- 比
fruits[fruits.length - 1]更优雅
with() - 修改指定索引的值(不修改原数组,ES2023)
javascript
const fruits = ['apple', 'banana', 'orange'];
const newFruits = fruits.with(1, 'grape');
console.log(newFruits); // ['apple', 'grape', 'orange']
console.log(fruits); // ['apple', 'banana', 'orange'] 原数组不变
toSpliced() - 删除、插入或替换(不修改原数组,ES2023)
javascript
const fruits = ['apple', 'banana', 'orange'];
const newFruits = fruits.toSpliced(1, 1, 'grape', 'mango');
console.log(newFruits); // ['apple', 'grape', 'mango', 'orange']
console.log(fruits); // ['apple', 'banana', 'orange'] 原数组不变
方法对比与选择
修改原数组 vs 不修改原数组
| 修改原数组 | 不修改原数组 |
|---|---|
| push, pop, shift, unshift | map, filter, slice |
| splice, sort, reverse | concat, join, toString |
| fill, copyWithin | flat, flatMap |
| toSorted, toReversed, with, toSpliced |
选择建议:
- 优先使用不修改原数组的方法(函数式编程)
- 需要修改原数组时,明确知道副作用
查找方法对比
| 方法 | 返回值 | 使用场景 |
|---|---|---|
| indexOf | 索引或 -1 | 简单值查找 |
| includes | 布尔值 | 判断是否存在 |
| find | 元素或 undefined | 对象查找 |
| findIndex | 索引或 -1 | 对象查找索引 |
| some | 布尔值 | 判断是否有满足条件的元素 |
遍历方法对比
| 方法 | 返回值 | 是否可中断 | 使用场景 |
|---|---|---|---|
| forEach | undefined | 否 | 简单遍历 |
| map | 新数组 | 否 | 转换数组 |
| filter | 新数组 | 否 | 过滤数组 |
| for...of | - | 是 | 需要中断的遍历 |
| reduce | 单个值 | 否 | 归约计算 |
最佳实践
1. 链式调用
javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 链式调用多个方法
const result = numbers
.filter(num => num % 2 === 0) // [2, 4, 6, 8, 10]
.map(num => num * 2) // [4, 8, 12, 16, 20]
.reduce((sum, num) => sum + num, 0); // 60
console.log(result); // 60
2. 避免在遍历中修改数组
javascript
// ❌ 不推荐:在 forEach 中修改数组长度
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((num, index) => {
if (num % 2 === 0) {
numbers.splice(index, 1); // 危险!会跳过元素
}
});
// ✅ 推荐:使用 filter
const numbers2 = [1, 2, 3, 4, 5];
const odds = numbers2.filter(num => num % 2 !== 0);
3. 使用解构赋值
javascript
// 交换变量
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2, 1
// 获取第一个和剩余元素
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]
4. 性能优化
javascript
// ❌ 不推荐:多次遍历
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);
const doubled = evens.map(n => n * 2);
const sum = doubled.reduce((a, b) => a + b, 0);
// ✅ 推荐:单次遍历
const sum2 = numbers
.filter(n => n % 2 === 0)
.map(n => n * 2)
.reduce((a, b) => a + b, 0);
// ✅ 更推荐:使用 reduce 一次完成
const sum3 = numbers.reduce((acc, n) => {
if (n % 2 === 0) {
return acc + n * 2;
}
return acc;
}, 0);
5. 处理空值和边界情况
javascript
// 安全的数组操作
function safeArrayOperation(arr) {
if (!Array.isArray(arr) || arr.length === 0) {
return [];
}
return arr.map(item => item * 2);
}
// 使用可选链和空值合并
const result = array?.map(x => x * 2) ?? [];
6. 数组去重
javascript
// 方法 1:使用 Set
const unique1 = [...new Set([1, 2, 2, 3, 3, 3])];
console.log(unique1); // [1, 2, 3]
// 方法 2:使用 filter + indexOf
const unique2 = [1, 2, 2, 3, 3, 3].filter((item, index, arr) =>
arr.indexOf(item) === index
);
// 方法 3:对象数组去重
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice' }
];
const uniqueUsers = users.filter((user, index, arr) =>
arr.findIndex(u => u.id === user.id) === index
);
console.log(uniqueUsers); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
// 方法 4:使用 Map(性能更好)
const uniqueUsers2 = Array.from(
new Map(users.map(user => [user.id, user])).values()
);
7. 数组分组
javascript
// 使用 reduce 实现分组
const users = [
{ name: 'Alice', age: 25, city: 'Beijing' },
{ name: 'Bob', age: 30, city: 'Shanghai' },
{ name: 'Charlie', age: 25, city: 'Beijing' },
{ name: 'David', age: 30, city: 'Shanghai' }
];
// 按城市分组
const groupedByCity = users.reduce((acc, user) => {
const city = user.city;
if (!acc[city]) {
acc[city] = [];
}
acc[city].push(user);
return acc;
}, {});
console.log(groupedByCity);
// {
// Beijing: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
// Shanghai: [{ name: 'Bob', ... }, { name: 'David', ... }]
// }
// 按年龄分组
const groupedByAge = users.reduce((acc, user) => {
const age = user.age;
acc[age] = acc[age] || [];
acc[age].push(user);
return acc;
}, {});
8. 数组分块
javascript
// 将数组分割成指定大小的块
function chunk(array, size) {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(chunk(numbers, 3)); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 使用 reduce 实现
const chunk2 = (array, size) =>
array.reduce((acc, _, i) => {
if (i % size === 0) {
acc.push(array.slice(i, i + size));
}
return acc;
}, []);
9. 数组合并
javascript
// concat() - 合并数组(不修改原数组)
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = arr1.concat(arr2);
console.log(arr3); // [1, 2, 3, 4, 5, 6]
console.log(arr1); // [1, 2, 3] 原数组不变
// 使用扩展运算符(推荐)
const arr4 = [...arr1, ...arr2];
console.log(arr4); // [1, 2, 3, 4, 5, 6]
// 合并多个数组
const arr5 = [7, 8, 9];
const merged = [...arr1, ...arr2, ...arr5];
console.log(merged); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
10. 数组交集、并集、差集
javascript
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [4, 5, 6, 7, 8];
// 交集:两个数组都有的元素
const intersection = arr1.filter(item => arr2.includes(item));
console.log(intersection); // [4, 5]
// 并集:两个数组的所有元素(去重)
const union = [...new Set([...arr1, ...arr2])];
console.log(union); // [1, 2, 3, 4, 5, 6, 7, 8]
// 差集:arr1 有但 arr2 没有的元素
const difference = arr1.filter(item => !arr2.includes(item));
console.log(difference); // [1, 2, 3]
// 对称差集:两个数组独有的元素
const symmetricDifference = [
...arr1.filter(item => !arr2.includes(item)),
...arr2.filter(item => !arr1.includes(item))
];
console.log(symmetricDifference); // [1, 2, 3, 6, 7, 8]
11. 数组随机打乱
javascript
// Fisher-Yates 洗牌算法
function shuffle(array) {
const arr = [...array]; // 避免修改原数组
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const numbers = [1, 2, 3, 4, 5];
console.log(shuffle(numbers)); // 随机顺序
console.log(numbers); // [1, 2, 3, 4, 5] 原数组不变
12. 数组扁平化(多种方法)
javascript
const nested = [1, [2, 3], [4, [5, 6]]];
// 方法 1:使用 flat()
const flat1 = nested.flat(Infinity);
// 方法 2:使用 reduce + concat
const flat2 = nested.reduce((acc, cur) =>
acc.concat(Array.isArray(cur) ? flat2(cur) : cur), []
);
// 方法 3:使用扩展运算符(仅一层)
const flat3 = [].concat(...nested);
// 方法 4:使用 toString()(仅适用于数字)
const numbers = [[1, 2], [3, 4]];
const flat4 = numbers.toString().split(',').map(Number);
13. 数组统计
javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 求和
const sum = numbers.reduce((a, b) => a + b, 0);
// 平均值
const average = numbers.reduce((a, b) => a + b, 0) / numbers.length;
// 最大值和最小值
const max = Math.max(...numbers);
const min = Math.min(...numbers);
// 使用 reduce 求最大值和最小值
const max2 = numbers.reduce((a, b) => Math.max(a, b));
const min2 = numbers.reduce((a, b) => Math.min(a, b));
// 统计元素出现次数
const count = numbers.reduce((acc, num) => {
acc[num] = (acc[num] || 0) + 1;
return acc;
}, {});
14. 数组转对象
javascript
// 键值对数组转对象
const entries = [['name', 'Alice'], ['age', 25], ['city', 'Beijing']];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'Alice', age: 25, city: 'Beijing' }
// 对象数组转对象(以某个属性为键)
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});
console.log(userMap);
// {
// 1: { id: 1, name: 'Alice' },
// 2: { id: 2, name: 'Bob' },
// 3: { id: 3, name: 'Charlie' }
// }
15. 数组补全方法补充
concat() - 合并数组
javascript
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 合并数组
const merged = arr1.concat(arr2);
console.log(merged); // [1, 2, 3, 4, 5, 6]
// 可以合并多个数组
const arr3 = [7, 8, 9];
const all = arr1.concat(arr2, arr3);
console.log(all); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 也可以合并值
const withValues = arr1.concat(4, 5, [6, 7]);
console.log(withValues); // [1, 2, 3, 4, 5, 6, 7]
特点:
- 不修改原数组
- 返回新数组
- 可以合并多个数组或值
常见应用场景
场景 1:数据处理和转换
javascript
// 从 API 获取数据后处理
const apiData = [
{ id: 1, name: 'Alice', status: 'active' },
{ id: 2, name: 'Bob', status: 'inactive' },
{ id: 3, name: 'Charlie', status: 'active' }
];
// 提取活跃用户的名字
const activeUsers = apiData
.filter(user => user.status === 'active')
.map(user => user.name);
console.log(activeUsers); // ['Alice', 'Charlie']
场景 2:表单验证
javascript
// 验证表单字段
const formFields = [
{ name: 'username', value: 'alice', required: true },
{ name: 'email', value: '', required: true },
{ name: 'age', value: '25', required: false }
];
// 检查必填字段是否都已填写
const isValid = formFields
.filter(field => field.required)
.every(field => field.value.trim() !== '');
console.log(isValid); // false(email 为空)
场景 3:购物车计算
javascript
const cart = [
{ id: 1, name: '商品A', price: 100, quantity: 2 },
{ id: 2, name: '商品B', price: 200, quantity: 1 },
{ id: 3, name: '商品C', price: 50, quantity: 3 }
];
// 计算总价
const total = cart.reduce((sum, item) =>
sum + item.price * item.quantity, 0
);
console.log(total); // 450
// 计算商品总数
const totalQuantity = cart.reduce((sum, item) =>
sum + item.quantity, 0
);
console.log(totalQuantity); // 6
场景 4:数据筛选和搜索
javascript
const products = [
{ id: 1, name: 'iPhone', category: '手机', price: 5000 },
{ id: 2, name: 'iPad', category: '平板', price: 3000 },
{ id: 3, name: 'MacBook', category: '电脑', price: 10000 }
];
// 按价格筛选
const affordable = products.filter(p => p.price < 5000);
console.log(affordable); // [{ id: 2, name: 'iPad', ... }]
// 搜索功能
function searchProducts(products, keyword) {
return products.filter(product =>
product.name.toLowerCase().includes(keyword.toLowerCase())
);
}
console.log(searchProducts(products, 'phone'));
// [{ id: 1, name: 'iPhone', ... }]
场景 5:数据分组和聚合
javascript
const orders = [
{ date: '2024-01-01', amount: 100, category: '电子产品' },
{ date: '2024-01-01', amount: 50, category: '食品' },
{ date: '2024-01-02', amount: 200, category: '电子产品' },
{ date: '2024-01-02', amount: 30, category: '食品' }
];
// 按日期分组并计算总金额
const dailyTotal = orders.reduce((acc, order) => {
const date = order.date;
acc[date] = (acc[date] || 0) + order.amount;
return acc;
}, {});
console.log(dailyTotal);
// { '2024-01-01': 150, '2024-01-02': 230 }
// 按类别分组
const categoryTotal = orders.reduce((acc, order) => {
const category = order.category;
acc[category] = (acc[category] || 0) + order.amount;
return acc;
}, {});
console.log(categoryTotal);
// { '电子产品': 300, '食品': 80 }
性能注意事项
1. 方法选择
javascript
// ❌ 性能较差:多次遍历
const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(n => n % 2 === 0);
const doubled = evens.map(n => n * 2);
// ✅ 性能较好:单次遍历
const doubled2 = numbers.reduce((acc, n) => {
if (n % 2 === 0) {
acc.push(n * 2);
}
return acc;
}, []);
2. 大数组处理
javascript
// 对于大数组,考虑使用 for 循环
const largeArray = new Array(1000000).fill(0).map((_, i) => i);
// ❌ 可能较慢
const result1 = largeArray.map(x => x * 2);
// ✅ 可能更快
const result2 = [];
for (let i = 0; i < largeArray.length; i++) {
result2[i] = largeArray[i] * 2;
}
3. 提前退出
javascript
// 使用 some() 或 find() 可以提前退出
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// ✅ 找到第一个偶数就停止
const firstEven = numbers.find(n => n % 2 === 0);
// ❌ 会遍历所有元素
const firstEven2 = numbers.filter(n => n % 2 === 0)[0];
总结
JavaScript 数组提供了丰富而强大的方法,掌握这些方法可以大大提高开发效率。以下是关键要点:
核心方法分类
- 增删改查 :
push,pop,shift,unshift,splice,slice - 遍历转换 :
forEach,map,filter,reduce - 查找判断 :
find,findIndex,includes,some,every - 排序反转 :
sort,reverse,toSorted,toReversed - 转换方法 :
join,flat,flatMap,concat
选择原则
- 优先使用不修改原数组的方法(函数式编程)
- 根据需求选择合适的方法 (查找用
find,判断用some/every) - 链式调用提高代码可读性
- 注意性能,大数组考虑使用传统循环
最佳实践
- ✅ 使用
map转换数组 - ✅ 使用
filter过滤数组 - ✅ 使用
reduce进行复杂计算 - ✅ 使用
find查找元素 - ✅ 使用
includes判断存在性 - ❌ 避免在遍历中修改数组
- ❌ 避免不必要的多次遍历
学习建议
- 理解每个方法的返回值:是否修改原数组、返回什么类型
- 掌握链式调用:组合使用多个方法
- 熟悉常见场景:去重、分组、统计等
- 注意边界情况:空数组、undefined、null 等
- 关注性能:大数组时选择合适的方法
希望这篇指南能帮助你更好地掌握 JavaScript 数组方法,在实际开发中灵活运用!