跳到主要内容JavaScript 数组核心方法详解 | 极客日志JavaScriptNode.js大前端算法
JavaScript 数组核心方法详解
综述由AI生成JavaScript 数组的核心内置方法,涵盖迭代遍历(map、filter、reduce 等)、修改原数组(splice、push 等)及返回新数组(slice、concat 等)三大类。重点解析了各方法的返回值、可变性及适用场景,通过基础与实战示例对比了 map 与 forEach、slice 与 splice 的区别,并补充了 reduce 初始值的影响及手动实现思路。旨在帮助开发者提升代码可读性,避免状态管理中的副作用问题,并应对高频面试考点。
草莓泡芙23 浏览 一、核心概念
JavaScript 数组的内置方法是开发中最常用的工具,也是面试高频考点。核心关注点包括:
- 功能:方法的核心作用
- 返回值:执行后返回什么(新数组 / 单个值 / undefined)
- 可变性:是否修改原数组(mutability)
- 使用场景:与其他方法的对比和选型
| 分类 | 包含方法 | 核心特征 |
|---|
| 迭代遍历方法 | forEach、map、filter、reduce、some、every | 遍历数组元素并执行逻辑 |
| 修改原数组(Mutator) | push、pop、shift、unshift、splice、sort、reverse | 直接改变原数组,有副作用 |
| 返回新数组(Non-mutating) | map、filter、concat、slice | 原数组不变,返回新数组 |
| 查找判断方法 | find、findIndex、includes、indexOf | 查找元素或判断存在性 |
二、为什么要深入理解?
- 代码简洁高效:用高阶方法替代冗余的 for 循环,提升代码可读性和维护性;
- 避免副作用:明确可变性,防止在 React/Vue/Redux 等状态管理中误改原数据;
- 面试核心考点:手动实现 map/reduce、区分 slice/splice、判断方法可变性是高频面试题;
- 性能优化:不同方法的执行效率不同(如 reduce 比嵌套循环更高效)。
三、核心方法详解(附多场景示例)
1. 迭代遍历方法
(1) map() - 数组转换(返回新数组)
工作中最常使用。
- 核心:遍历数组,对每个元素执行回调,返回新数组(长度与原数组一致)
- 可变性:不修改原数组
- 返回值:新数组
- 适用场景:数据格式转换、批量处理元素
基础示例:
const numbers = [1, 4, 9];
const doubles = numbers.map(num => num * 2);
console.log(doubles);
console.log(numbers);
const users = [
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 25 },
{ id: 3, name: '王五', age: 30 }
];
const userNames = users.map(user => user.name);
console.log(userNames);
const adultUsers = users.map(user => ({ ...user, isAdult: user.age >= 18 }));
console.log(adultUsers[0]);
(2) filter() - 数组过滤(返回新数组)
- 核心:遍历数组,筛选出符合条件的元素,返回新数组
- 可变性:不修改原数组
- 返回值:新数组(长度≤原数组)
- 适用场景:数据筛选、条件过滤
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction'];
const longWords = words.filter(word => word.length > 6);
console.log(longWords);
[].filter(Boolean)
const products = [
{ name: '手机', price: 2999, category: '数码', stock: 50 },
{ name: '耳机', price: 199, category: '数码', stock: 0 },
{ name: 'T 恤', price: 99, category: '服饰', stock: 100 },
{ name: '键盘', price: 499, category: '数码', stock: 30 }
];
const validProducts = products.filter(p => p.category === '数码' && p.price < 500 && p.stock > 0 );
console.log(validProducts);
(3) reduce() - 数组归并(返回单个值)
- 核心:遍历数组,将元素'累积'为单个值(数字、对象、数组等)
- API:
arr.reduce((acc, cur, idx, arr) => {}, initialValue)
acc:累加器(上一次回调的返回值 / 初始值)
cur:当前元素
initialValue:可选,累加器初始值(推荐必传)
- 可变性:不修改原数组
- 适用场景:求和 / 求积、数组转对象、扁平数组、分组统计
const array = [1, 2, 3, 4];
const sum = array.reduce((acc, cur) => acc + cur, 0);
console.log(sum);
const users = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
];
const userMap = users.reduce((acc, cur) => {
acc[cur.id] = cur;
return acc;
}, {});
console.log(userMap[2]);
const nestedArr = [1, [2, [3, 4], 5], 6];
const flatArr = nestedArr.reduce((acc, cur) => {
return acc.concat(Array.isArray(cur) ? cur.reduce((a, c) => a.concat(c), []) : cur);
}, []);
console.log(flatArr);
const scores = [
{ name: '张三', subject: '数学', score: 90 },
{ name: '张三', subject: '语文', score: 85 },
{ name: '李四', subject: '数学', score: 88 },
{ name: '李四', subject: '语文', score: 92 }
];
const scoreSum = scores.reduce((acc, cur) => {
if (!acc[cur.name]) { acc[cur.name] = 0; }
acc[cur.name] += cur.score;
return acc;
}, {});
console.log(scoreSum);
(4) forEach() - 遍历执行(无返回值)
- 核心:遍历数组,对每个元素执行回调(仅执行操作,无返回值)
- 可变性:本身不修改原数组,但回调中可手动修改
- 返回值:undefined
- 适用场景:执行副作用操作(如打印、调用 API、修改 DOM)
const fruits = ['苹果', '香蕉', '橙子'];
fruits.forEach((fruit, index) => {
console.log(`第${index+1}个水果:${fruit}`);
});
(5) some()/every() - 条件判断
- some:只要有一个元素满足条件,返回 true(短路遍历)
- every:所有元素满足条件,返回 true(短路遍历)
- 返回值:布尔值
- 适用场景:判断数组是否符合某个条件
const numbers = [10, 20, 30, 35];
const hasBigNum = numbers.some(num => num > 30);
console.log(hasBigNum);
const allBigNum = numbers.every(num => num > 15);
console.log(allBigNum);
const orders = [
{ id: 1, expired: false },
{ id: 2, expired: true },
{ id: 3, expired: false }
];
const hasExpired = orders.some(order => order.expired);
console.log(hasExpired);
2. 修改原数组的方法(Mutator)
(1) splice() - 增删改数组(核心)
- 核心:删除 / 插入 / 替换数组元素(原地修改)
- API:
arr.splice(start, deleteCount, item1, item2...)
start:起始索引(负数表示从末尾开始)
deleteCount:删除的元素数量(0 则不删除)
item...:要插入的元素
- 返回值:被删除的元素组成的数组
- 可变性:修改原数组
const months = ['Jan', 'March', 'April', 'June'];
const removed = months.splice(1, 0, 'Feb');
console.log(months);
console.log(removed);
const arr = [1, 2, 3, 4, 5];
const deleted = arr.splice(2, 2);
console.log(arr);
console.log(deleted);
const arr = ['a', 'b', 'c', 'd'];
arr.splice(1, 1, 'x', 'y');
console.log(arr);
(2) push/pop/shift/unshift - 数组首尾操作
| 方法 | 功能 | 返回值 | 示例 |
|---|
| push | 尾部添加元素 | 新数组长度 | const arr = [1,2]; arr.push(3); // arr=[1,2,3],返回 3 |
| pop | 尾部删除元素 | 被删除的元素 | const arr = [1,2,3]; arr.pop(); // arr=[1,2],返回 3 |
| unshift | 头部添加元素 | 新数组长度 | const arr = [2,3]; arr.unshift(1); // arr=[1,2,3],返回 3 |
| shift | 头部删除元素 | 被删除的元素 | const arr = [1,2,3]; arr.shift(); // arr=[2,3],返回 1 |
(3) sort/reverse - 排序 / 反转
- sort:原地排序,默认按字符串 Unicode 码排序(需传回调自定义规则)
- reverse:原地反转数组顺序
const numbers = [10, 2, 25, 5];
numbers.sort((a, b) => a - b);
console.log(numbers);
const arr = [1, 2, 3];
arr.reverse();
console.log(arr);
3. 返回新数组的方法(Non-mutating)
(1) slice() - 截取数组片段
- 核心:截取数组的指定范围,返回新数组(浅拷贝)
- API:
arr.slice(start, end)(start 包含,end 不包含,负数表示末尾)
- 可变性:不修改原数组
- 返回值:新数组
const arr = [1, 2, 3, 4, 5];
const slice1 = arr.slice(1, 3);
console.log(slice1);
const slice2 = arr.slice(-2);
console.log(slice2);
const copyArr = arr.slice();
copyArr[0] = 10;
console.log(arr);
(2) concat() - 数组合并
- 核心:合并多个数组 / 值,返回新数组
- 可变性:不修改原数组
- 返回值:新数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = arr1.concat(arr2, 5);
console.log(arr3);
console.log(arr1);
四、易混淆方法对比
(1) map vs forEach
| 维度 | map | forEach |
|---|
| 返回值 | 新数组 | undefined |
| 用途 | 数据转换,需返回新数组 | 执行操作(打印、调 API),无需返回值 |
| 链式调用 | 支持(map().filter()) | 不支持(返回 undefined) |
(2) slice vs splice
| 维度 | slice | splice |
|---|
| 可变性 | 不修改原数组 | 修改原数组 |
| 功能 | 截取数组片段 | 增删改数组元素 |
| 参数 | slice(start, end) | splice(start, deleteCount, ...items) |
| 返回值 | 截取的新数组 | 被删除的元素数组 |
(3) reduce 有无 initialValue 的区别
| 场景 | 有 initialValue | 无 initialValue |
|---|
| 空数组 | 返回 initialValue | 抛出 TypeError |
| 非空数组 | acc 初始值 = initialValue,从第一个元素开始遍历 | acc 初始值 = 第一个元素,从第二个元素开始遍历 |
[1,2,3].reduce((acc, cur) => acc + cur, 0);
[1,2,3].reduce((acc, cur) => acc + cur);
五、关键注意事项
- 可变性避坑:
- React/Vue 中,禁止直接修改 state 数组(如
state.arr.push(1)),需用非变异方法([...state.arr, 1]);
- 如需修改原数组,先浅拷贝(
const copy = [...arr])再操作。
- 稀疏数组处理:
- map/forEach/filter 会跳过稀疏数组的空位(
[1,,3].map(x => x*2) → [2,,6]);
- reduce 也会跳过空位(除非传 initialValue)。
- 性能优化:
- 避免嵌套迭代(如 map 里套 filter),优先用 reduce 一次遍历;
- 大数据量遍历,for 循环比 forEach/map 更快(高阶方法有函数调用开销)。
六、手动实现核心方法(面试必考)
Array.prototype.myMap = function(callback) {
const newArr = [];
for (let i = 0; i < this.length; i++) {
if (i in this) {
newArr[i] = callback(this[i], i, this);
}
}
return newArr;
};
Array.prototype.myReduce = function(callback, initialValue) {
let acc = initialValue;
let startIndex = 0;
if (initialValue === undefined) {
if (this.length === 0) throw new TypeError('空数组无初始值');
acc = this[0];
startIndex = 1;
}
for (let i = startIndex; i < this.length; i++) {
if (i in this) {
acc = callback(acc, this[i], i, this);
}
}
return acc;
};
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online