04-数组操作全解
分类:01-JavaScript基础核心
发布于:
阅读时间:48 分钟
数组操作全解
📋 学习目标
- 掌握数组的多种创建方式
- 熟练使用数组API进行增删改查操作
- 理解迭代方法与高阶函数的应用
- 掌握数组性能优化技巧
🎯 数组基础
1. 数组创建方式
数组字面量(推荐)
// 空数组
let arr1 = [];
// 带初始值的数组
let arr2 = [1, 2, 3];
let arr3 = ['apple', 'banana', 'orange'];
let arr4 = [1, 'hello', true, null, undefined, {obj: 'object'}];
// 多维数组
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
Array构造函数
// 创建空数组
let arr1 = new Array();
// 创建指定长度的数组
let arr2 = new Array(3); // [empty × 3]
console.log(arr2.length); // 3
// 创建带元素的数组
let arr3 = new Array(1, 2, 3); // [1, 2, 3]
// ⚠️ 陷阱:单个数字参数表示长度
let arr4 = new Array(3); // [empty × 3] 不是 [3]
let arr5 = new Array('3'); // ['3']
ES6新增方法
// Array.of() - 总是返回参数组成的数组
let arr1 = Array.of(3); // [3]
let arr2 = Array.of(1, 2, 3); // [1, 2, 3]
// Array.from() - 将类数组转换为数组
let arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
let arr3 = Array.from(arrayLike); // ['a', 'b', 'c']
// 转换字符串
let arr4 = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
// 带映射函数
let arr5 = Array.from([1, 2, 3], x => x * 2); // [2, 4, 6]
🔧 数组操作方法
1. 添加和删除元素
修改原数组的方法
let fruits = ['apple', 'banana'];
// push() - 末尾添加
fruits.push('orange'); // ['apple', 'banana', 'orange']
let length = fruits.push('grape', 'melon'); // 返回新长度
// pop() - 末尾删除
let lastFruit = fruits.pop(); // 'melon'
// unshift() - 开头添加
fruits.unshift('strawberry'); // ['strawberry', 'apple', 'banana', 'orange']
// shift() - 开头删除
let firstFruit = fruits.shift(); // 'strawberry'
// splice() - 任意位置删除、插入、替换
let numbers = [1, 2, 3, 4, 5];
// 删除元素
numbers.splice(2, 1); // 从索引2开始删除1个元素 -> [1, 2, 4, 5]
// 插入元素
numbers.splice(2, 0, 3); // 在索引2处插入3 -> [1, 2, 3, 4, 5]
// 替换元素
numbers.splice(1, 2, 1.5, 2.5); // 替换索引1开始的2个元素
不修改原数组的方法
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
// concat() - 连接数组
let combined = arr1.concat(arr2); // [1, 2, 3, 4, 5, 6]
let combined2 = arr1.concat(7, 8, [9, 10]); // [1, 2, 3, 7, 8, 9, 10]
// slice() - 截取数组
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// 基本用法
let slice1 = numbers.slice(2, 5); // [3, 4, 5] (包前不包后)
// 省略第二个参数
let slice2 = numbers.slice(3); // [4, 5, 6, 7, 8, 9]
// 负索引
let slice3 = numbers.slice(-3); // [7, 8, 9]
let slice4 = numbers.slice(2, -2); // [3, 4, 5, 6, 7]
// 复制整个数组
let copy = numbers.slice(); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
2. 查找和过滤
基础查找方法
let users = [
{ id: 1, name: '张三', age: 25, active: true },
{ id: 2, name: '李四', age: 30, active: false },
{ id: 3, name: '王五', age: 35, active: true }
];
// indexOf() - 查找元素索引
let arr = ['apple', 'banana', 'orange'];
let index1 = arr.indexOf('banana'); // 1
let index2 = arr.indexOf('grape'); // -1 (未找到)
// lastIndexOf() - 从后向前查找
let index3 = arr.lastIndexOf('apple'); // 0
// includes() - 判断是否包含
let hasBanana = arr.includes('banana'); // true
let hasGrape = arr.includes('grape'); // false
// find() - 查找第一个匹配的元素
let activeUser = users.find(user => user.active); // { id: 1, name: '张三', age: 25, active: true }
// findIndex() - 查找第一个匹配元素的索引
let activeIndex = users.findIndex(user => user.active); // 0
过滤和判断方法
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// filter() - 过滤元素
let evenNumbers = numbers.filter(num => num % 2 === 0); // [2, 4, 6, 8, 10]
let adults = users.filter(user => user.age >= 30); // [{ id: 2, ... }, { id: 3, ... }]
// some() - 至少有一个满足条件
let hasEven = numbers.some(num => num % 2 === 0); // true
let hasAdult = users.some(user => user.age < 18); // false
// every() - 所有元素都满足条件
let allEven = numbers.every(num => num % 2 === 0); // false
let allActive = users.every(user => user.active); // false
3. 转换和遍历
map() - 转换数组
let numbers = [1, 2, 3, 4, 5];
// 基础映射
let doubled = numbers.map(num => num * 2); // [2, 4, 6, 8, 10]
// 对象数组映射
let userNames = users.map(user => user.name); // ['张三', '李四', '王五']
// 复杂映射
let userCards = users.map(user => ({
id: user.id,
fullName: user.name,
isAdult: user.age >= 18,
status: user.active ? '在线' : '离线'
}));
forEach() - 遍历数组
let fruits = ['apple', 'banana', 'orange'];
// 基础遍历
fruits.forEach((fruit, index) => {
console.log(`${index + 1}. ${fruit}`);
});
// 带副作用(修改外部变量)
let sum = 0;
numbers.forEach(num => {
sum += num;
});
console.log('总和:', sum);
reduce() - 累积计算
let numbers = [1, 2, 3, 4, 5];
// 求和
let sum = numbers.reduce((acc, num) => acc + num, 0); // 15
// 求最大值
let max = numbers.reduce((acc, num) => Math.max(acc, num), -Infinity); // 5
// 数组分组
let people = [
{ name: '张三', age: 25 },
{ name: '李四', age: 30 },
{ name: '王五', age: 25 }
];
let groupedByAge = people.reduce((acc, person) => {
let age = person.age;
if (!acc[age]) {
acc[age] = [];
}
acc[age].push(person);
return acc;
}, {});
// { 25: [{ name: '张三', age: 25 }, { name: '王五', age: 25 }], 30: [{ name: '李四', age: 30 }] }
// 数组去重
let duplicates = [1, 2, 2, 3, 4, 4, 5];
let unique = duplicates.reduce((acc, num) => {
if (!acc.includes(num)) {
acc.push(num);
}
return acc;
}, []); // [1, 2, 3, 4, 5]
4. 排序和反转
let numbers = [3, 1, 4, 1, 5, 9, 2, 6];
// sort() - 排序(会修改原数组)
let sorted = numbers.slice().sort(); // [1, 1, 2, 3, 4, 5, 6, 9] (字符串排序)
// 数字排序
numbers.sort((a, b) => a - b); // [1, 1, 2, 3, 4, 5, 6, 9] (数字排序)
// 降序排序
numbers.sort((a, b) => b - a); // [9, 6, 5, 4, 3, 2, 1, 1]
// 对象数组排序
users.sort((a, b) => a.age - b.age); // 按年龄升序
users.sort((a, b) => b.age - a.age); // 按年龄降序
users.sort((a, b) => a.name.localeCompare(b.name)); // 按姓名排序
// reverse() - 反转数组
let reversed = numbers.slice().reverse(); // [9, 6, 5, 4, 3, 2, 1, 1]
5. ES6+ 新增方法
// flat() - 数组扁平化
let nested = [1, [2, 3], [4, [5, 6]]];
let flat1 = nested.flat(); // [1, 2, 3, 4, [5, 6]]
let flat2 = nested.flat(2); // [1, 2, 3, 4, 5, 6] (指定深度)
// flatMap() - 映射后扁平化
let sentences = ['Hello World', 'JavaScript is Fun'];
let words = sentences.flatMap(sentence => sentence.split(' '));
// ['Hello', 'World', 'JavaScript', 'is', 'Fun']
// entries(), keys(), values() - 获取迭代器
let arr = ['a', 'b', 'c'];
for (let index of arr.keys()) {
console.log(index); // 0, 1, 2
}
for (let value of arr.values()) {
console.log(value); // 'a', 'b', 'c'
}
for (let [index, value] of arr.entries()) {
console.log(index, value); // 0 'a', 1 'b', 2 'c'
}
// fill() - 填充数组
let fillArr = new Array(5).fill(0); // [0, 0, 0, 0, 0]
let fillArr2 = [1, 2, 3, 4, 5];
fillArr2.fill(0, 1, 3); // [1, 0, 0, 4, 5] (从索引1到3填充0)
🚀 高级应用
1. 数组去重的多种方法
let arr = [1, 2, 2, 3, 4, 4, 5, '1', '2'];
// 方法1:Set + Array.from
let unique1 = Array.from(new Set(arr));
// 方法2:扩展运算符
let unique2 = [...new Set(arr)];
// 方法3:filter + indexOf
let unique3 = arr.filter((item, index) => arr.indexOf(item) === index);
// 方法4:reduce
let unique4 = arr.reduce((acc, item) => {
return acc.includes(item) ? acc : [...acc, item];
}, []);
// 方法5:对象去重(适用于对象数组)
let users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 1, name: '张三(重复)' }
];
let uniqueUsers = Array.from(new Map(users.map(user => [user.id, user])).values());
2. 数组分块
// 将数组分块
function chunk(array, size) {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let chunks = chunk(numbers, 3); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// 使用reduce实现分块
const chunkReduce = (array, size) => {
return array.reduce((acc, cur, i) => {
const index = Math.floor(i / size);
if (!acc[index]) {
acc[index] = [];
}
acc[index].push(cur);
return acc;
}, []);
};
3. 数组性能优化
// 大数组操作优化
let largeArray = Array.from({ length: 1000000 }, (_, i) => i);
// ❌ 性能较差
let slowFilter = largeArray.filter(x => x % 2 === 0).map(x => x * 2);
// ✅ 性能较好(减少中间数组)
let fastMap = largeArray.reduce((acc, x) => {
if (x % 2 === 0) {
acc.push(x * 2);
}
return acc;
}, []);
// 使用for循环处理超大数组(性能最佳)
function processLargeArray(array) {
const result = [];
for (let i = 0; i < array.length; i++) {
if (array[i] % 2 === 0) {
result.push(array[i] * 2);
}
}
return result;
}
⚠️ 常见陷阱
1. 引用类型的问题
// ❌ 浅拷贝问题
let original = [{ name: '对象1' }, { name: '对象2' }];
let copy = original.slice();
copy[0].name = '修改后的对象1';
console.log(original[0].name); // '修改后的对象1' (原数组也被修改)
// ✅ 深拷贝
let deepCopy = JSON.parse(JSON.stringify(original));
let deepCopy2 = original.map(item => ({ ...item }));
2. 稀疏数组陷阱
let sparse = [1, , 3]; // 稀疏数组
console.log(sparse.length); // 3
console.log(sparse[1]); // undefined
// map会跳过空位
let mapped = sparse.map(x => x * 2); // [2, empty, 6]
// filter会移除空位
let filtered = sparse.filter(x => true); // [1, 3]
3. 数组边界问题
let arr = [1, 2, 3];
// 越界访问不会报错
console.log(arr[5]); // undefined
// splice负索引
arr.splice(-1, 1); // 从最后一个开始删除
📝 最佳实践
- 优先使用函数式方法:map、filter、reduce等
- 避免直接修改原数组:使用slice、扩展运算符创建副本
- 选择合适的数据结构:频繁查找时考虑使用Map或Set
- 注意性能:大数组操作时考虑使用传统for循环
- 类型安全:使用TypeScript增加类型检查
🎯 小结
- 掌握了数组的多种创建方式
- 熟练使用各种数组API进行操作
- 理解了迭代方法和函数式编程
- 学会了数组性能优化技巧
- 了解了常见陷阱和解决方案
下一步学习:字符串处理方法