讲解
数组实例方法分为两派:不改变原数组的(map、filter、slice、concat、flat、join、find、indexOf……)和改变原数组的(push、pop、splice、sort、reverse、fill……)。现代 JavaScript 更推崇不可变风格,ES2023 还补齐了 sort/reverse/splice 的不可变版本(toSorted/toReversed/toSpliced,见最后一章)。写代码时心里要清楚手上这个方法属于哪一派。
转换三剑客是日常开发的核心:map 把每个元素变换成新元素(一对一),filter 按条件筛选(多对少),reduce 把数组聚合成一个值(多对一)。reduce 功能最强也最难读,能用 map/filter 表达的就不要用 reduce。查找一族包括 find(找第一个满足条件的元素)、findIndex(找下标)、includes(是否包含)、some(是否存在满足条件的)、every(是否全部满足)。
sort 有个著名陷阱:默认按字符串字典序排,[10, 9, 80].sort() 得到 [10, 80, 9]——因为 '10' < '80' < '9'。数字排序必须传比较函数:sort((a, b) => a - b),且 sort 会修改原数组。这些方法的回调都会收到 (元素, 下标, 原数组) 三个参数,多数情况只用第一个。链式调用是惯用风格:arr.filter(...).map(...).join(...) 一气呵成。
示例
const scores = [58, 92, 75, 44, 88];
// map:一对一变换
const curved = scores.map((s) => s + 5);
console.log('加分后:', curved);
// filter:筛选
const passed = scores.filter((s) => s >= 60);
console.log('及格:', passed);
// reduce:聚合
const total = scores.reduce((acc, s) => acc + s, 0);
console.log('总分:', total, '平均:', total / scores.length);
// 链式:及格分数各加 5 分再降序
const result = scores
.filter((s) => s >= 60)
.map((s) => s + 5)
.sort((a, b) => b - a);
console.log('链式结果:', result);
// 查找
console.log('第一个不及格:', scores.find((s) => s < 60));
console.log('有满分吗:', scores.some((s) => s === 100));
console.log('全都及格吗:', scores.every((s) => s >= 60));
// sort 的字符串陷阱
console.log('默认排序:', [10, 9, 80].sort());
console.log('数字排序:', [10, 9, 80].sort((a, b) => a - b));
常见坑
- sort 不传比较函数排数字:默认字典序会让 [10, 9, 80] 排成 [10, 80, 9],且 sort 原地修改原数组。
- 在 forEach 里用 return 想跳出循环:forEach 的 return 只跳过当前回调,不能 break。要提前退出用 for...of 或 some/find。
- map 回调里写副作用:map 应该纯变换,只做遍历用 forEach 或 for...of;看到 arr.map(x => console.log(x)) 就该改成 forEach。
- reduce 不写初始值:空数组上没有初始值的 reduce 会抛 TypeError。永远写第二个参数,它还能明确结果类型。
小结
map 变换、filter 筛选、reduce 聚合,find/some/every 查找,sort 记得传比较函数且会改原数组。链式调用让数据处理一气呵成。下一节讲另一种核心结构:对象。