摘要:本文梳理了JavaScript数组操作方法,涵盖增删改查、遍历转换、排序去重等功能。通过代码示例、使用场景、缺陷分析及纵向延伸问答,巩固对JavaScrpt数组的认知及日常使用备忘。
1. push() 与 pop()
概述
push():在数组末尾添加元素,返回新长度。pop():删除并返回最后一个元素。- 直接修改原数组。
代码示例
let arr = [1, 2, 3];
arr.push(4); // arr: [1, 2, 3, 4]
arr.pop(); // 返回 4,arr: [1, 2, 3]
使用场景
- 实现栈(先进后出)逻辑,如撤销/重做功能。
- 动态追加数据(如消息队列末尾)。
缺陷分析
push()可能导致内存溢出(大数组慎用)。pop()删除后无法恢复,需手动备份。
纵向延伸问答
Q:如何实现类似 push() 的不可变操作?
A:使用扩展运算符 arr = [...arr, 4],生成新数组。
2. unshift() 与 shift()
概述
unshift():在数组开头添加元素,返回新长度。shift():删除并返回第一个元素。- 直接修改原数组。
代码示例
let arr = [1, 2, 3];
arr.unshift(0); // arr: [0, 1, 2, 3]
arr.shift(); // 返回 0,arr: [1, 2, 3]
使用场景
- 实现队列(先进先出)逻辑,如任务调度。
- 优先级插入(如高优先级任务前置)。
缺陷分析
unshift()/shift()性能较差(需重排索引)。- 大数组频繁操作会导致性能瓶颈。
纵向延伸问答
Q:如何高效实现队列的头部插入?
A:使用双端队列(Deque)或链表结构替代数组。
3. splice()
概述
- 从任意位置删除/添加元素,返回被删除的元素数组。
- 直接修改原数组。
代码示例
let arr = [1, 2, 3, 4];
let removed = arr.splice(1, 2, 'a', 'b');
// arr: [1, "a", "b", 4], removed: [2, 3]
使用场景
- 灵活编辑数组中间内容(如表格数据更新)。
- 批量删除无效数据(如过滤特定索引范围)。
缺陷分析
- 参数复杂(需明确起始索引、删除数量)。
- 大规模操作可能导致性能问题。
纵向延伸问答
Q:如何用 splice() 实现数组去重?
A:结合 indexOf() 判断重复项,例如:
for (let i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) < i) arr.splice(i, 1);
}
4. slice()
概述
- 返回指定区间的浅拷贝子数组,不修改原数组。
代码示例
let arr = [1, 2, 3, 4];
let sub = arr.slice(1, 3); // [2, 3]
使用场景
- 分页数据截取(如
slice(0, 10))。 - 浅拷贝数组(
slice()无参数时)。
缺陷分析
- 浅拷贝问题(嵌套数组或对象需深拷贝)。
- 额外内存开销(生成新数组)。
纵向延伸问答
Q:如何实现深拷贝?
A:结合 JSON.parse(JSON.stringify(arr)) 或递归复制。
5. map()
概述
- 对每个元素执行回调,返回新数组。
- 不修改原数组。
代码示例
let nums = [1, 2, 3];
let squares = nums.map(x => x * x); // [1, 4, 9]
使用场景
- 数据转换(如单位换算)。
- 生成派生数组(如 HTML 元素列表)。
缺陷分析
- 无副作用(仅适合纯函数场景)。
- 大数组可能占用较多内存。
纵向延伸问答
Q:map() 和 forEach() 的区别?
A:map() 返回新数组,forEach() 无返回值;map() 适合映射,forEach() 适合副作用操作。
6. filter()
概述
- 过滤满足条件的元素,返回新数组。
- 不修改原数组。
代码示例
let nums = [1, 2, 3, 4];
let even = nums.filter(x => x % 2 === 0); // [2, 4]
使用场景
- 条件筛选(如搜索过滤)。
- 移除无效数据(如空值清理)。
缺陷分析
- 无法处理
NaN(NaN !== NaN)。 - 大数组可能占用较多内存。
纵向延伸问答
Q:如何过滤唯一值?
A:结合 Set:[...new Set(arr)]。
7. reduce()
概述
- 累积器处理数组,返回单一值(如总和)。
- 不修改原数组。
代码示例
let sum = [1, 2, 3].reduce((acc, curr) => acc + curr, 0); // 6
使用场景
- 聚合计算(如求和、平均值)。
- 数据扁平化(嵌套数组合并)。
缺陷分析
- 初值处理需谨慎(未提供初值时以第一个元素为初值)。
- 回调逻辑复杂时易出错。
纵向延伸问答
Q:如何用 reduce() 实现数组分组?
A:
let grouped = arr.reduce((acc, item) => {
let key = item.category;
acc[key] = acc[key] || [];
acc[key].push(item);
return acc;
}, {});
8. sort()
概述
- 对数组排序(默认按字符串 Unicode 排序)。
- 直接修改原数组。
代码示例
let nums = [3, 1, 4];
nums.sort(); // [1, 3, 4]
nums.sort((a, b) => b - a); // [4, 3, 1]
使用场景
- 数据排序(如排行榜)。
- 自定义排序规则(如对象属性排序)。
缺陷分析
- 默认排序逻辑可能不符合预期(需自定义比较函数)。
- 修改原数组(需先复制)。
纵向延伸问答
Q:如何稳定排序?
A:在比较函数中加入索引判断,确保相同值的顺序不变。
9. includes()
概述
- 判断数组是否包含某元素,返回布尔值。
代码示例
let arr = [1, 2, 3];
console.log(arr.includes(2)); // true
使用场景
- 快速判断元素存在性(如权限校验)。
- 替代
indexOf()的更直观写法。
缺陷分析
- 无法处理
NaN(NaN !== NaN)。 - 无参数时默认从索引0开始查找。
纵向延伸问答
Q:如何判断 NaN 是否存在?
A:使用 Number.isNaN() 或 Object.is()。
10. Array.from()
概述
- 从类数组对象或可迭代对象创建数组。
代码示例
let arr = Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
使用场景
- 转换类数组对象(如
arguments、NodeList)。 - 生成固定长度数组(如
[...Array(5)])。
缺陷分析
- 非可迭代对象会抛异常(如
null、undefined)。
纵向延伸问答
Q:如何用 Array.from() 生成指定范围的数字数组?
A:
Array.from({length: 5}, (_, i) => i + 1); // [1, 2, 3, 4, 5]
11. flat() 与 flatMap()
概述
flat():展开嵌套数组。flatMap():先映射再展开。
代码示例
let arr = [1, [2, [3]]];
let flat = arr.flat(2); // [1, 2, 3]
let flatMap = arr.flatMap(x => x); // [1, 2, [3]]
使用场景
- 嵌套数据扁平化(如多维数组处理)。
- 映射后展开(如字符串拆分后去空)。
缺陷分析
flat()默认展开深度为1,需指定参数。flatMap()可能返回嵌套数组(需二次展开)。
12. find() 与 findIndex()
概述
find():返回满足条件的第一个元素。findIndex():返回满足条件的第一个元素的索引。
代码示例
let users = [{id:1, name:'Alice'}, {id:2, name:'Bob'}];
let user = users.find(u => u.id === 2); // {id:2, name:'Bob'}
let index = users.findIndex(u => u.id === 1); // 0
使用场景
- 查询特定元素(如用户 ID 搜索)。
- 快速定位索引(如删除指定元素)。
缺陷分析
- 仅返回第一个匹配项(需遍历全部时需额外处理)。
- 未找到时返回
undefined或-1,需判断。
13. some() 与 every()
概述
some():至少一个元素满足条件返回true。every():所有元素满足条件返回true。
代码示例
let nums = [1, 2, 3];
console.log(nums.some(x => x > 2)); // true
console.log(nums.every(x => x > 0)); // true
使用场景
- 条件验证(如表单必填字段检查)。
- 数据完整性校验(如所有用户状态为激活)。
缺陷分析
- 无法获取具体满足条件的元素。
- 大数组遍历可能影响性能。
14. fill() 与 copyWithin()
概述
fill():填充数组指定区间。copyWithin():复制数组内部元素到指定位置。
代码示例
let arr = [1, 2, 3, 4];
arr.fill(0, 1, 3); // [1, 0, 0, 4]
arr.copyWithin(2, 0, 2); // [1, 0, 1, 0]
使用场景
- 初始化数组(如
[0,0,0])。 - 快速复制数组片段(如数据滑动)。
缺陷分析
fill()会覆盖原数组,不可逆。copyWithin()参数易混淆(目标索引、起始索引、结束索引)。
汇总总结
| 方法 | 是否修改原数组 | 主要用途 | 注意事项 |
|---|---|---|---|
push() | ✅ | 添加元素到末尾 | 性能问题(大数组慎用) |
pop() | ✅ | 删除末尾元素 | 无法恢复删除值 |
unshift() | ✅ | 添加元素到开头 | 性能问题(大数组慎用) |
shift() | ✅ | 删除开头元素 | 无法恢复删除值 |
splice() | ✅ | 灵活增删改 | 参数复杂 |
slice() | ❌ | 提取子数组 | 浅拷贝问题 |
map() | ❌ | 数据映射 | 生成新数组 |
filter() | ❌ | 条件筛选 | 生成新数组 |
reduce() | ❌ | 累积计算 | 初值处理需谨慎 |
sort() | ✅ | 自定义排序 | 默认排序逻辑需注意 |
includes() | ❌ | 快速判断元素是否存在 | 无法处理 NaN |
Array.from() | ❌ | 转换类数组对象 | 依赖可迭代性 |
flat() | ❌ | 展开嵌套数组 | 默认展开深度为1 |
find() | ❌ | 查询特定元素 | 仅返回第一个匹配项 |
关键建议
-
修改原数组 vs 生成新数组:
- 若需保留原数组,优先使用
slice()、map()等非修改方法。 - 若需高效操作,可直接使用
push()、splice()等修改方法。
- 若需保留原数组,优先使用
-
性能优化:
- 避免在
unshift()/shift()中处理大数组(性能较差)。 reduce()和map()在链式调用时需注意内存占用。
- 避免在
-
兼容性:
includes()、flat()等 ES6 方法需注意旧环境兼容性(如 IE 不支持)。
-
深度理解:
- 掌握方法底层原理(如
reduce()的累积器逻辑)。 - 结合实际场景选择最优解(如
filter()过滤 vssplice()删除)。
- 掌握方法底层原理(如
通过系统掌握数组方法及其特性,开发者可高效处理数据操作需求,同时避免常见错误,提升代码质量与性能。