我将为您完善数组方法详解的文档,详细介绍JavaScript数组的各种内置方法。
---
title: 数组方法详解
icon: javascript
order: 2
---
# 数组方法详解
JavaScript数组提供了丰富的内置方法,用于添加、删除、查找、排序和转换数组元素。本文将详细介绍这些方法的用法、参数和返回值,帮助您高效地操作数组。
## 修改数组的方法
以下方法会直接修改原始数组:
### 添加和删除元素
#### push()
在数组末尾添加一个或多个元素,并返回新的数组长度。
```javascript
const fruits = ['苹果', '香蕉'];
const newLength = fruits.push('橙子', '芒果');
console.log(fruits); // ['苹果', '香蕉', '橙子', '芒果']
console.log(newLength); // 4
pop()
移除并返回数组的最后一个元素。
const fruits = ['苹果', '香蕉', '橙子'];
const lastFruit = fruits.pop();
console.log(fruits); // ['苹果', '香蕉']
console.log(lastFruit); // '橙子'
unshift()
在数组开头添加一个或多个元素,并返回新的数组长度。
const fruits = ['香蕉', '橙子'];
const newLength = fruits.unshift('苹果', '草莓');
console.log(fruits); // ['苹果', '草莓', '香蕉', '橙子']
console.log(newLength); // 4
shift()
移除并返回数组的第一个元素。
const fruits = ['苹果', '香蕉', '橙子'];
const firstFruit = fruits.shift();
console.log(fruits); // ['香蕉', '橙子']
console.log(firstFruit); // '苹果'
splice()
通过删除、替换或添加元素来修改数组,并返回被删除的元素。
const fruits = ['苹果', '香蕉', '橙子', '芒果', '葡萄'];
// 删除元素:从索引1开始删除2个元素
const removed1 = fruits.splice(1, 2);
console.log(fruits); // ['苹果', '芒果', '葡萄']
console.log(removed1); // ['香蕉', '橙子']
// 替换元素:从索引1开始删除1个元素,并插入新元素
const removed2 = fruits.splice(1, 1, '草莓', '蓝莓');
console.log(fruits); // ['苹果', '草莓', '蓝莓', '葡萄']
console.log(removed2); // ['芒果']
// 添加元素:从索引1开始,删除0个元素,并插入新元素
fruits.splice(1, 0, '西瓜');
console.log(fruits); // ['苹果', '西瓜', '草莓', '蓝莓', '葡萄']
数组排序
sort()
对数组元素进行排序,并返回排序后的数组。默认按照字符串Unicode码点排序。
// 字符串排序
const fruits = ['香蕉', '苹果', '橙子', '芒果'];
fruits.sort();
console.log(fruits); // ['橙子', '苹果', '芒果', '香蕉']
// 数字排序(默认会有问题)
const numbers = [10, 5, 40, 25, 1];
numbers.sort();
console.log(numbers); // [1, 10, 25, 40, 5] - 错误的排序,因为按字符串处理
// 使用比较函数进行数字排序
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 5, 10, 25, 40] - 正确的升序排序
// 降序排序
numbers.sort((a, b) => b - a);
console.log(numbers); // [40, 25, 10, 5, 1]
reverse()
颠倒数组中元素的顺序,并返回修改后的数组。
const fruits = ['苹果', '香蕉', '橙子'];
fruits.reverse();
console.log(fruits); // ['橙子', '香蕉', '苹果']
填充数组
fill()
用固定值填充数组中的元素,可以指定起始和结束位置。
// 填充整个数组
const array1 = [1, 2, 3, 4, 5];
array1.fill(0);
console.log(array1); // [0, 0, 0, 0, 0]
// 指定起始位置
const array2 = [1, 2, 3, 4, 5];
array2.fill(0, 2);
console.log(array2); // [1, 2, 0, 0, 0]
// 指定起始和结束位置(不包括结束位置)
const array3 = [1, 2, 3, 4, 5];
array3.fill(0, 1, 4);
console.log(array3); // [1, 0, 0, 0, 5]
copyWithin()
将数组的一部分复制到同一数组中的另一个位置,并返回修改后的数组。
const array = [1, 2, 3, 4, 5];
// 将索引3开始的元素复制到索引0的位置
array.copyWithin(0, 3);
console.log(array); // [4, 5, 3, 4, 5]
// 将索引1到索引3(不包括3)的元素复制到索引2的位置
const array2 = [1, 2, 3, 4, 5];
array2.copyWithin(2, 1, 3);
console.log(array2); // [1, 2, 2, 3, 5]
不修改原数组的方法
以下方法不会修改原始数组,而是返回一个新的数组或其他值:
数组连接和切片
concat()
合并两个或多个数组,返回一个新数组。
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
const array3 = [7, 8, 9];
const newArray = array1.concat(array2, array3);
console.log(newArray); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(array1); // [1, 2, 3] - 原数组不变
slice()
返回数组的一部分的浅拷贝,不修改原数组。
const fruits = ['苹果', '香蕉', '橙子', '芒果', '葡萄'];
// 从索引1到索引3(不包括3)
const citrus = fruits.slice(1, 3);
console.log(citrus); // ['香蕉', '橙子']
// 从索引2到末尾
const end = fruits.slice(2);
console.log(end); // ['橙子', '芒果', '葡萄']
// 复制整个数组
const copy = fruits.slice();
console.log(copy); // ['苹果', '香蕉', '橙子', '芒果', '葡萄']
// 使用负索引(从末尾计数)
const last = fruits.slice(-2);
console.log(last); // ['芒果', '葡萄']
数组搜索和过滤
indexOf() 和 lastIndexOf()
查找元素在数组中的索引。
const fruits = ['苹果', '香蕉', '橙子', '香蕉', '芒果'];
// 查找元素的第一个匹配项
console.log(fruits.indexOf('香蕉')); // 1
console.log(fruits.indexOf('西瓜')); // -1(未找到)
// 从指定位置开始查找
console.log(fruits.indexOf('香蕉', 2)); // 3
// 查找元素的最后一个匹配项
console.log(fruits.lastIndexOf('香蕉')); // 3
includes()
判断数组是否包含指定元素,返回布尔值。
const fruits = ['苹果', '香蕉', '橙子'];
console.log(fruits.includes('香蕉')); // true
console.log(fruits.includes('西瓜')); // false
// 从指定位置开始查找
console.log(fruits.includes('香蕉', 2)); // false
find() 和 findIndex()
根据提供的测试函数查找数组中的元素或其索引。
const users = [
{ id: 1, name: '张三', age: 28 },
{ id: 2, name: '李四', age: 22 },
{ id: 3, name: '王五', age: 35 }
];
// 查找第一个年龄大于30的用户
const user = users.find(user => user.age > 30);
console.log(user); // { id: 3, name: '王五', age: 35 }
// 查找第一个年龄大于30的用户的索引
const index = users.findIndex(user => user.age > 30);
console.log(index); // 2
// 未找到时的返回值
const notFound = users.find(user => user.age > 40);
console.log(notFound); // undefined
const notFoundIndex = users.findIndex(user => user.age > 40);
console.log(notFoundIndex); // -1
findLast() 和 findLastIndex() (ES2023)
从数组末尾开始,根据提供的测试函数查找数组中的元素或其索引。
const numbers = [5, 12, 8, 130, 44];
// 查找最后一个大于10的数字
const lastLarge = numbers.findLast(num => num > 10);
console.log(lastLarge); // 44
// 查找最后一个大于10的数字的索引
const lastLargeIndex = numbers.findLastIndex(num => num > 10);
console.log(lastLargeIndex); // 4
filter()
创建一个新数组,包含通过测试函数的所有元素。
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 过滤出所有偶数
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6, 8, 10]
// 过滤出所有大于5的数字
const largeNumbers = numbers.filter(num => num > 5);
console.log(largeNumbers); // [6, 7, 8, 9, 10]
// 复杂对象过滤
const users = [
{ id: 1, name: '张三', age: 28, active: true },
{ id: 2, name: '李四', age: 22, active: false },
{ id: 3, name: '王五', age: 35, active: true }
];
const activeUsers = users.filter(user => user.active);
console.log(activeUsers); // [{ id: 1, name: '张三', age: 28, active: true }, { id: 3, name: '王五', age: 35, active: true }]
数组转换和映射
map()
创建一个新数组,其结果是对原数组中的每个元素调用提供的函数。
const numbers = [1, 2, 3, 4, 5];
// 将每个元素乘以2
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 对象数组转换
const users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
];
const usernames = users.map(user => user.name);
console.log(usernames); // ['张三', '李四', '王五']
// 复杂转换
const formatted = users.map(user => ({
id: user.id,
name: user.name,
label: `用户${user.id}: ${user.name}`
}));
console.log(formatted);
flatMap()
首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。
const sentences = ['Hello world', 'JavaScript is fun'];
// 将每个句子拆分为单词,并压平结果
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'JavaScript', 'is', 'fun']
// 相当于先map再flat
const mapThenFlat = sentences.map(sentence => sentence.split(' ')).flat();
console.log(mapThenFlat); // ['Hello', 'world', 'JavaScript', 'is', 'fun']
flat()
创建一个新数组,将嵌套数组扁平化到指定深度。
const nestedArray = [1, 2, [3, 4, [5, 6]]];
// 默认扁平化一层
const flattened1 = nestedArray.flat();
console.log(flattened1); // [1, 2, 3, 4, [5, 6]]
// 指定扁平化深度
const flattened2 = nestedArray.flat(2);
console.log(flattened2); // [1, 2, 3, 4, 5, 6]
// 完全扁平化(无论嵌套多深)
const deeplyNested = [1, [2, [3, [4, [5]]]]];
const fullyFlattened = deeplyNested.flat(Infinity);
console.log(fullyFlattened); // [1, 2, 3, 4, 5]
数组迭代方法
forEach()
对数组的每个元素执行一次给定的函数。
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((num, index, array) => {
console.log(`索引 ${index}: ${num}`);
});
// 输出:
// 索引 0: 1
// 索引 1: 2
// 索引 2: 3
// 索引 3: 4
// 索引 4: 5
// 注意:forEach不能通过常规方式中断循环
// 也不会返回值
every()
测试数组中的所有元素是否都通过了指定函数的测试,返回布尔值。
const numbers = [1, 2, 3, 4, 5];
// 检查是否所有元素都大于0
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true
// 检查是否所有元素都大于3
const allGreaterThan3 = numbers.every(num => num > 3);
console.log(allGreaterThan3); // false
// 一旦找到不满足条件的元素,立即返回false并停止遍历
some()
测试数组中是否至少有一个元素通过了指定函数的测试,返回布尔值。
const numbers = [1, 2, 3, 4, 5];
// 检查是否存在大于3的元素
const hasGreaterThan3 = numbers.some(num => num > 3);
console.log(hasGreaterThan3); // true
// 检查是否存在负数
const hasNegative = numbers.some(num => num < 0);
console.log(hasNegative); // false
// 一旦找到满足条件的元素,立即返回true并停止遍历
数组归约方法
reduce()
对数组中的每个元素执行一个由您提供的reducer函数,将其结果汇总为单个返回值。
const numbers = [1, 2, 3, 4, 5];
// 计算数组元素的总和
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 15
// 带初始值的reduce
const initialValue = 10;
const sumWithInitial = numbers.reduce(
(accumulator, currentValue) => accumulator + currentValue,
initialValue
);
console.log(sumWithInitial); // 25
// 计算数组中每个元素出现的次数
const fruits = ['苹果', '香蕉', '苹果', '橙子', '香蕉', '苹果'];
const countMap = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(countMap); // { '苹果': 3, '香蕉': 2, '橙子': 1 }
// 数组扁平化
const nestedArray = [[1, 2], [3, 4], [5, 6]];
const flattened = nestedArray.reduce(
(acc, curr) => acc.concat(curr),
[]
);
console.log(flattened); // [1, 2, 3, 4, 5, 6]
reduceRight()
类似于reduce(),但从右到左处理数组。
const numbers = [1, 2, 3, 4, 5];
// 从右到左连接字符串
const result = numbers.reduceRight(
(accumulator, currentValue) => accumulator + currentValue,
''
);
console.log(result); // '54321'
// 与reduce比较
const leftToRight = numbers.reduce(
(accumulator, currentValue) => accumulator + currentValue,
''
);
console.log(leftToRight); // '12345'
数组的静态方法
Array.isArray()
判断传递的值是否是一个数组。
console.log(Array.isArray([1, 2, 3])); // true
console.log(Array.isArray('hello')); // false
console.log(Array.isArray({})); // false
console.log(Array.isArray(null)); // false
Array.from()
从类数组对象或可迭代对象创建一个新的数组实例。
// 从字符串创建数组
console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o']
// 从Set创建数组
const set = new Set([1, 2, 3, 2, 1]);
console.log(Array.from(set)); // [1, 2, 3]
// 使用映射函数
console.log(Array.from([1, 2, 3], x => x * 2)); // [2, 4, 6]
// 创建指定长度的数组并填充
console.log(Array.from({ length: 5 }, (_, i) => i)); // [0, 1, 2, 3, 4]
Array.of()
创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
console.log(Array.of(7)); // [7]
console.log(Array.of(1, 2, 3)); // [1, 2, 3]
console.log(Array.of()); // []
// 与Array构造函数的区别
console.log(new Array(7)); // [empty × 7]
console.log(new Array(1, 2, 3)); // [1, 2, 3]
数组方法的高级应用
链式调用
许多数组方法返回数组,因此可以链式调用这些方法:
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = numbers
.filter(num => num % 2 === 0) // 过滤出偶数
.map(num => num * 2) // 每个数乘以2
.reduce((sum, num) => sum + num, 0); // 计算总和
console.log(result); // 60 (2+4+6+8+10)*2
函数式编程
数组方法是JavaScript中函数式编程的重要组成部分:
// 使用函数组合处理数据
const users = [
{ id: 1, name: '张三', age: 28 },
{ id: 2, name: '李四', age: 22 },
{ id: 3, name: '王五', age: 35 },
{ id: 4, name: '赵六', age: 42 }
];
// 获取所有年龄大于30的用户的名字,并按字母顺序排序
const names = users
.filter(user => user.age > 30)
.map(user => user.name)
.sort();
console.log(names); // ['王五', '赵六']
性能考虑
不同的数组方法有不同的性能特性:
// 避免在循环中使用数组方法
const numbers = Array.from({ length: 1000 }, (_, i) => i);
// 低效方式:每次迭代都创建新数组
let sum1 = 0;
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] % 2 === 0) {
sum1 += numbers[i] * 2;
}
}
// 高效方式:使用单次遍历
let sum2 = numbers.reduce((sum, num) => {
if (num % 2 === 0) {
return sum + num * 2;
}
return sum;
}, 0);
// 两种方式结果相同,但第二种更高效
console.log(sum1 === sum2); // true
数组方法的浏览器兼容性
大多数现代浏览器都支持本文介绍的数组方法,但在使用较新的方法(如flat()
、flatMap()
、findLast()
等)时,应注意兼容性问题。
对于需要支持旧版浏览器的项目,可以使用polyfill或转译工具(如Babel)来确保兼容性。
总结
JavaScript数组提供了丰富的内置方法,可以分为以下几类:
- 修改原数组的方法:
push()
、pop()
、shift()
、unshift()
、splice()
、sort()
、reverse()
、fill()
、copyWithin()
- 不修改原数组的方法:
- 数组连接和切片:
concat()
、slice()
- 搜索和过滤:
indexOf()
、lastIndexOf()
、includes()
、find()
、findIndex()
、findLast()
、findLastIndex()
、filter()
- 转换和映射:
map()
、flatMap()
、flat()
- 迭代方法:
forEach()
、every()
、some()
- 归约方法:
reduce()
、reduceRight()
- 数组连接和切片:
- 静态方法:
Array.isArray()
、Array.from()
、Array.of()
掌握这些方法可以帮助您更高效地处理数组数据,编写更简洁、更可读的代码。在实际应用中,应根据具体需求选择合适的方法,并考虑性能和兼容性因素。