JavaScript数组方法
JavaScript数组提供了丰富的内置方法,用于数组的创建、操作和转换。本文将详细介绍这些方法的使用方式和实际应用场景。
数组创建
字面量创建
javascript
// 使用数组字面量
const fruits = ["苹果", "香蕉", "橙子"];
const numbers = [1, 2, 3, 4, 5];
const mixed = [1, "二", true, null, {name: "张三"}];
const empty = [];
构造函数创建
javascript
// 使用Array构造函数
const fruits = new Array("苹果", "香蕉", "橙子");
const numbers = new Array(5); // 创建长度为5的空数组
const filledArray = new Array(3).fill(0); // [0, 0, 0]
其他创建方法
javascript
// Array.from - 从类数组对象或可迭代对象创建数组
const arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 };
const fromArrayLike = Array.from(arrayLike); // ["a", "b", "c"]
// 从字符串创建数组
const fromString = Array.from("hello"); // ["h", "e", "l", "l", "o"]
// 使用映射函数
const mapped = Array.from([1, 2, 3], x => x * 2); // [2, 4, 6]
// Array.of - 从参数创建数组
const ofNumbers = Array.of(1, 2, 3, 4); // [1, 2, 3, 4]
const ofSingle = Array.of(5); // [5],与new Array(5)不同
数组基本操作
访问元素
javascript
const fruits = ["苹果", "香蕉", "橙子"];
// 使用索引访问
console.log(fruits[0]); // "苹果"
console.log(fruits[1]); // "香蕉"
console.log(fruits[2]); // "橙子"
console.log(fruits[3]); // undefined(超出范围)
// 使用at()方法(ES2022)
console.log(fruits.at(0)); // "苹果"
console.log(fruits.at(-1)); // "橙子"(支持负索引)
console.log(fruits.at(-2)); // "香蕉"
修改元素
javascript
const fruits = ["苹果", "香蕉", "橙子"];
// 使用索引修改
fruits[1] = "葡萄";
console.log(fruits); // ["苹果", "葡萄", "橙子"]
// 添加新元素
fruits[3] = "西瓜";
console.log(fruits); // ["苹果", "葡萄", "橙子", "西瓜"]
// 稀疏数组(不推荐)
fruits[10] = "芒果";
console.log(fruits); // ["苹果", "葡萄", "橙子", "西瓜", empty × 6, "芒果"]
console.log(fruits.length); // 11
数组长度
javascript
const fruits = ["苹果", "香蕉", "橙子"];
console.log(fruits.length); // 3
// 修改长度可以截断数组
fruits.length = 2;
console.log(fruits); // ["苹果", "香蕉"]
// 增加长度会创建空槽位
fruits.length = 5;
console.log(fruits); // ["苹果", "香蕉", empty × 3]
添加和删除元素
末尾操作
javascript
const fruits = ["苹果", "香蕉"];
// push - 在末尾添加一个或多个元素,返回新长度
const newLength = fruits.push("橙子", "葡萄");
console.log(newLength); // 4
console.log(fruits); // ["苹果", "香蕉", "橙子", "葡萄"]
// pop - 移除并返回末尾元素
const lastFruit = fruits.pop();
console.log(lastFruit); // "葡萄"
console.log(fruits); // ["苹果", "香蕉", "橙子"]
开头操作
javascript
const fruits = ["香蕉", "橙子"];
// unshift - 在开头添加一个或多个元素,返回新长度
const newLength = fruits.unshift("苹果", "葡萄");
console.log(newLength); // 4
console.log(fruits); // ["苹果", "葡萄", "香蕉", "橙子"]
// shift - 移除并返回开头元素
const firstFruit = fruits.shift();
console.log(firstFruit); // "苹果"
console.log(fruits); // ["葡萄", "香蕉", "橙子"]
任意位置操作
javascript
const fruits = ["苹果", "香蕉", "橙子", "葡萄", "西瓜"];
// splice - 删除、替换或添加元素
// 语法: array.splice(start, deleteCount, item1, item2, ...)
// 删除元素
const removed1 = fruits.splice(1, 2); // 从索引1开始删除2个元素
console.log(removed1); // ["香蕉", "橙子"]
console.log(fruits); // ["苹果", "葡萄", "西瓜"]
// 替换元素
const removed2 = fruits.splice(1, 1, "芒果", "蓝莓");
console.log(removed2); // ["葡萄"]
console.log(fruits); // ["苹果", "芒果", "蓝莓", "西瓜"]
// 插入元素(不删除)
const removed3 = fruits.splice(2, 0, "草莓");
console.log(removed3); // [](没有删除元素)
console.log(fruits); // ["苹果", "芒果", "草莓", "蓝莓", "西瓜"]
查找元素
按值查找
javascript
const fruits = ["苹果", "香蕉", "橙子", "香蕉"];
// indexOf - 返回第一个匹配元素的索引,未找到返回-1
console.log(fruits.indexOf("香蕉")); // 1
console.log(fruits.indexOf("香蕉", 2)); // 3(从索引2开始查找)
console.log(fruits.indexOf("梨")); // -1(未找到)
// lastIndexOf - 从后向前查找,返回第一个匹配元素的索引
console.log(fruits.lastIndexOf("香蕉")); // 3
console.log(fruits.lastIndexOf("香蕉", 2)); // 1(从索引2开始向前查找)
// includes - 检查数组是否包含某个元素,返回布尔值
console.log(fruits.includes("橙子")); // true
console.log(fruits.includes("梨")); // false
console.log(fruits.includes("香蕉", 2)); // true(从索引2开始查找)
按条件查找
javascript
const people = [
{ name: "张三", age: 25 },
{ name: "李四", age: 30 },
{ name: "王五", age: 20 }
];
// find - 返回第一个满足条件的元素,未找到返回undefined
const person1 = people.find(person => person.age > 25);
console.log(person1); // { name: "李四", age: 30 }
// findIndex - 返回第一个满足条件的元素的索引,未找到返回-1
const index = people.findIndex(person => person.name === "王五");
console.log(index); // 2
// findLast - 从后向前查找,返回第一个满足条件的元素(ES2023)
const person2 = people.findLast(person => person.age > 20);
console.log(person2); // { name: "李四", age: 30 }
// findLastIndex - 从后向前查找,返回第一个满足条件的元素的索引(ES2023)
const lastIndex = people.findLastIndex(person => person.age > 20);
console.log(lastIndex); // 1
检查条件
javascript
const numbers = [1, 2, 3, 4, 5];
// every - 检查所有元素是否都满足条件
const allPositive = numbers.every(num => num > 0);
console.log(allPositive); // true
const allEven = numbers.every(num => num % 2 === 0);
console.log(allEven); // false
// some - 检查是否至少有一个元素满足条件
const hasEven = numbers.some(num => num % 2 === 0);
console.log(hasEven); // true
const hasNegative = numbers.some(num => num < 0);
console.log(hasNegative); // false
数组转换与映射
映射
javascript
const numbers = [1, 2, 3, 4, 5];
// map - 对每个元素应用函数,返回新数组
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(numbers); // [1, 2, 3, 4, 5](原数组不变)
// 对象数组映射
const people = [
{ name: "张三", age: 25 },
{ name: "李四", age: 30 }
];
const names = people.map(person => person.name);
console.log(names); // ["张三", "李四"]
const descriptions = people.map(person => `${person.name}今年${person.age}岁`);
console.log(descriptions); // ["张三今年25岁", "李四今年30岁"]
过滤
javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// filter - 返回满足条件的元素组成的新数组
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
const greaterThanFive = numbers.filter(num => num > 5);
console.log(greaterThanFive); // [6, 7, 8, 9, 10]
// 过滤对象数组
const people = [
{ name: "张三", age: 25 },
{ name: "李四", age: 17 },
{ name: "王五", age: 30 },
{ name: "赵六", age: 16 }
];
const adults = people.filter(person => person.age >= 18);
console.log(adults); // [{ name: "张三", age: 25 }, { name: "王五", age: 30 }]
归约
javascript
const numbers = [1, 2, 3, 4, 5];
// reduce - 将数组归约为单个值
// 语法: array.reduce(callback(accumulator, currentValue, index, array), initialValue)
// 求和
const sum = numbers.reduce((total, num) => total + num, 0);
console.log(sum); // 15
// 求乘积
const product = numbers.reduce((result, num) => result * num, 1);
console.log(product); // 120
// 找最大值
const max = numbers.reduce((max, num) => Math.max(max, num), -Infinity);
console.log(max); // 5
// 数组扁平化
const nestedArrays = [[1, 2], [3, 4], [5]];
const flattened = nestedArrays.reduce((result, arr) => result.concat(arr), []);
console.log(flattened); // [1, 2, 3, 4, 5]
// 对象数组归约
const people = [
{ name: "张三", age: 25, salary: 10000 },
{ name: "李四", age: 30, salary: 15000 },
{ name: "王五", age: 28, salary: 12000 }
];
const totalSalary = people.reduce((total, person) => total + person.salary, 0);
console.log(totalSalary); // 37000
// 按属性分组
const peopleByAge = people.reduce((groups, person) => {
const age = person.age;
groups[age] = groups[age] || [];
groups[age].push(person);
return groups;
}, {});
console.log(peopleByAge);
// {
// 25: [{ name: "张三", age: 25, salary: 10000 }],
// 30: [{ name: "李四", age: 30, salary: 15000 }],
// 28: [{ name: "王五", age: 28, salary: 12000 }]
// }
扁平化
javascript
// flat - 将嵌套数组扁平化
const nestedArrays = [1, [2, 3], [4, [5, 6]]];
// 默认扁平化一层
const flattened1 = nestedArrays.flat();
console.log(flattened1); // [1, 2, 3, 4, [5, 6]]
// 指定扁平化深度
const flattened2 = nestedArrays.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]
// flatMap - 先映射再扁平化(深度为1)
const sentences = ["Hello world", "JavaScript is fun"];
const words = sentences.flatMap(sentence => sentence.split(" "));
console.log(words); // ["Hello", "world", "JavaScript", "is", "fun"]
数组排序
基本排序
javascript
const fruits = ["香蕉", "苹果", "橙子", "葡萄"];
// sort - 对数组元素进行排序(默认按字符串顺序)
fruits.sort();
console.log(fruits); // ["橙子", "苹果", "葡萄", "香蕉"]
// 数字排序(需要比较函数)
const numbers = [40, 100, 1, 5, 25, 10];
// 错误的数字排序(按字符串顺序)
numbers.sort();
console.log(numbers); // [1, 10, 100, 25, 40, 5]
// 正确的升序排序
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 5, 10, 25, 40, 100]
// 降序排序
numbers.sort((a, b) => b - a);
console.log(numbers); // [100, 40, 25, 10, 5, 1]
对象数组排序
javascript
const people = [
{ name: "张三", age: 25 },
{ name: "李四", age: 30 },
{ name: "王五", age: 20 }
];
// 按年龄升序排序
people.sort((a, b) => a.age - b.age);
console.log(people);
// [
// { name: "王五", age: 20 },
// { name: "张三", age: 25 },
// { name: "李四", age: 30 }
// ]
// 按名字字母顺序排序
people.sort((a, b) => a.name.localeCompare(b.name));
console.log(people);
// [
// { name: "李四", age: 30 },
// { name: "王五", age: 20 },
// { name: "张三", age: 25 }
// ]
反转数组
javascript
const numbers = [1, 2, 3, 4, 5];
// reverse - 反转数组元素的顺序
numbers.reverse();
console.log(numbers); // [5, 4, 3, 2, 1]
数组迭代
forEach
javascript
const fruits = ["苹果", "香蕉", "橙子"];
// forEach - 对每个元素执行函数,无返回值
fruits.forEach((fruit, index) => {
console.log(`${index}: ${fruit}`);
});
// 输出:
// 0: 苹果
// 1: 香蕉
// 2: 橙子
// 注意:forEach无法中断循环(除非抛出异常)
其他迭代方法
javascript
const numbers = [1, 2, 3, 4, 5];
// for...of循环(推荐)
for (const num of numbers) {
console.log(num);
}
// 传统for循环
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// entries(), keys(), values()
for (const [index, value] of numbers.entries()) {
console.log(`${index}: ${value}`);
}
for (const index of numbers.keys()) {
console.log(index);
}
for (const value of numbers.values()) {
console.log(value);
}
数组复制与合并
复制数组
javascript
const original = [1, 2, 3];
// 使用展开运算符(浅拷贝)
const copy1 = [...original];
// 使用slice(浅拷贝)
const copy2 = original.slice();
// 使用Array.from(浅拷贝)
const copy3 = Array.from(original);
// 使用concat(浅拷贝)
const copy4 = [].concat(original);
console.log(copy1); // [1, 2, 3]
合并数组
javascript
const array1 = [1, 2, 3];
const array2 = [4, 5, 6];
// 使用展开运算符
const merged1 = [...array1, ...array2];
console.log(merged1); // [1, 2, 3, 4, 5, 6]
// 使用concat
const merged2 = array1.concat(array2);
console.log(merged2); // [1, 2, 3, 4, 5, 6]
// 合并多个数组
const array3 = [7, 8, 9];
const multiMerged = [...array1, ...array2, ...array3];
console.log(multiMerged); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
数组转换
数组与字符串转换
javascript
const fruits = ["苹果", "香蕉", "橙子"];
// join - 将数组元素连接成字符串
const joined = fruits.join(", ");
console.log(joined); // "苹果, 香蕉, 橙子"
// 不同分隔符
console.log(fruits.join("")); // "苹果香蕉橙子"
console.log(fruits.join("-")); // "苹果-香蕉-橙子"
// 字符串转数组
const str = "苹果,香蕉,橙子";
const backToArray = str.split(",");
console.log(backToArray); // ["苹果", "香蕉", "橙子"]
数组转对象
javascript
const fruits = ["苹果", "香蕉", "橙子"];
// 使用Object.assign
const obj1 = Object.assign({}, fruits);
console.log(obj1); // { 0: "苹果", 1: "香蕉", 2: "橙子" }
// 使用展开运算符
const obj2 = { ...fruits };
console.log(obj2); // { 0: "苹果", 1: "香蕉", 2: "橙子" }
// 使用Object.fromEntries(键值对数组转对象)
const entries = [["name", "张三"], ["age", 30]];
const person = Object.fromEntries(entries);
console.log(person); // { name: "张三", age: 30 }
实用技巧
数组去重
javascript
const numbers = [1, 2, 2, 3, 4, 4, 5];
// 使用Set去重
const unique1 = [...new Set(numbers)];
console.log(unique1); // [1, 2, 3, 4, 5]
// 使用filter去重
const unique2 = numbers.filter((value, index, self) => {
return self.indexOf(value) === index;
});
console.log(unique2); // [1, 2, 3, 4, 5]
// 对象数组去重(按指定属性)
const people = [
{ id: 1, name: "张三" },
{ id: 2, name: "李四" },
{ id: 1, name: "张三" } // 重复的id
];
const uniquePeople = Array.from(
new Map(people.map(person => [person.id, person])).values()
);
console.log(uniquePeople);
// [
// { id: 1, name: "张三" },
// { id: 2, name: "李四" }
// ]
数组填充
javascript
// fill - 用固定值填充数组
const filled = new Array(5).fill(0);
console.log(filled); // [0, 0, 0, 0, 0]
// 部分填充
const numbers = [1, 2, 3, 4, 5];
numbers.fill(0, 2, 4); // 从索引2到索引4(不包括4)填充0
console.log(numbers); // [1, 2, 0, 0, 5]
// 创建序列数组
const sequence = Array.from({ length: 5 }, (_, i) => i + 1);
console.log(sequence); // [1, 2, 3, 4, 5]
数组切片
javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// slice - 返回数组的一部分,不修改原数组
// 语法: array.slice(start, end),包含start,不包含end
const sliced1 = numbers.slice(2, 5);
console.log(sliced1); // [3, 4, 5]
// 省略end参数,提取到末尾
const sliced2 = numbers.slice(7);
console.log(sliced2); // [8, 9, 10]
// 使用负索引
const sliced3 = numbers.slice(-3);
console.log(sliced3); // [8, 9, 10]
const sliced4 = numbers.slice(2, -2);
console.log(sliced4); // [3, 4, 5, 6, 7, 8]
数组分块
javascript
// 将数组分成指定大小的块
function chunk(array, size) {
const result = [];
for (let i = 0; i < array.length; i += size) {
result.push(array.slice(i, i + size));
}
return result;
}
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const chunked = chunk(numbers, 3);
console.log(chunked); // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
性能考虑
高效操作
javascript
const largeArray = Array.from({ length: 1000000 }, (_, i) => i);
// 避免在循环中修改数组长度
// 不好的做法
let badPractice = [];
for (let i = 0; i < 10000; i++) {
badPractice.push(i); // 每次都可能导致数组重新分配内存
}
// 更好的做法
let goodPractice = new Array(10000);
for (let i = 0; i < 10000; i++) {
goodPractice[i] = i;
}
// 使用适当的迭代方法
// 如果只需要遍历,for...of通常比forEach更高效
// 如果需要索引,传统for循环通常最高效
避免常见陷阱
javascript
// 避免使用delete操作符(会留下空槽)
const arr = [1, 2, 3, 4];
delete arr[1]; // 不推荐
console.log(arr); // [1, empty, 3, 4]
// 改用splice
const arr2 = [1, 2, 3, 4];
arr2.splice(1, 1);
console.log(arr2); // [1, 3, 4]
// 避免稀疏数组
const sparse = [1, , 3]; // 有空槽的数组
console.log(sparse.length); // 3
console.log(sparse); // [1, empty, 3]
// 某些方法会跳过空槽,某些不会
sparse.forEach(item => console.log(item)); // 只输出1和3
const mapped = sparse.map(x => x * 2); // [2, empty, 6]
// 但filter会移除空槽
const filtered = sparse.filter(() => true);
console.log(filtered); // [1, 3]
总结
JavaScript数组提供了丰富的方法,使得数组操作变得简单而强大。掌握这些方法可以帮助你更有效地处理数据,编写更简洁、更高效的代码。
关键要点:
- 数组是有序集合,可以存储任意类型的数据
- 添加/删除元素的主要方法:
push
/pop
、unshift
/shift
、splice
- 查找元素的方法:
indexOf
/lastIndexOf
、find
/findIndex
、includes
- 转换数组的方法:
map
、filter
、reduce
、flat
/flatMap
- 排序和操作顺序的方法:
sort
、reverse
- 迭代方法:
forEach
、for...of
循环 - 数组合并与复制:展开运算符、
concat
、slice
- 字符串转换:
join
、split
根据具体需求选择合适的数组方法,可以大大提高代码的可读性和性能。