告别 for 循环,JavaScript 数组遍历新形态


过去十年 JavaScript 在不断进化,新的数组方法不仅能让我们的代码更简洁易读的代码,还能帮我们避免一系列常见错误。

for 循环的问题

让我们先来看看一个经典的 for 循环场景,将一个数组中的每个数字翻倍:

const numbers = [1, 2, 3, 4, 5];  
const doubled = [];  
  
for (let i = 0; i < numbers.length; i++) {  
  doubled.push(numbers[i] * 2);  
}  
console.log(doubled); // [2, 4, 6, 8, 10]  

代码不仅冗余,读起来也像在描述过程,意图不清晰。

声明式与函数式

JavaScript 为我们提供了很多优秀的数组遍历方法。

1. 遍历执行: forEach

forEach for 循环最直接的替代品,用于对数组中的每个元素执行一次操作,但它不返回新的数组。

** 场景 ** :打印数组中的每一项

没有了索引 i 的管理,代码更干净,意图也更明确。

2. 数据转换: map

map 是最常用的数组方法之一。它会遍历数组中的每个元素,用一个函数对它进行处理,然后 ** 返回一个包含所有处理结果的新数组 ** 。

// 旧方法 (`for`) (见上文)  
// 新形态 (`map`)  
const numbers = [1, 2, 3, 4, 5];  
const doubled = numbers.map(number => number * 2);  
console.log(doubled); // [2, 4, 6, 8, 10]  

一行代码就完成了 for 循环五六行代码的工作,而且 map 不会修改原数组 numbers ,有效避免了副作用。

3. 数据筛选: filter

filter 方法会 ** 创建一个新数组 ** ,其中包含所有通过了由提供的函数实现的测试的元素。

** 场景 ** :从一个班级里筛选出所有成绩及格的学生

filter 这个词本身就完美地描述了它的功能,核心逻辑 student.score >= 60
非常突出,没有被循环的模板代码所淹没。

4. 数据聚合: reduce

reduce 是功能强大的数组方法,可以将一个数组转化为单一值(可以是数字、字符串、对象,甚至是另一个数组)。

** 场景 ** :计算数组中所有数字的总和

// 旧方法 (`for`)  
const numbers = [1, 2, 3, 4, 5];  
let sum = 0;  
for (let i = 0; i < numbers.length; i++) {  
  sum += numbers[i];  
}  
// 新形态 (`reduce`)  
const numbers = [1, 2, 3, 4, 5];  
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);  

链式调用

现代数组方法的真正威力在于它们的组合性, map filter 都返回新数组,所以我们可以将它们像链条一样串联起来。

const totalValue = numbers  
  .filter(number => number > 3)   
  .map(number => number * 2)  
  .reduce((total, value) => total + value, 0);  

for 循环到 map/filter/reduce
的转变,其背后是推动我们从关注“如何做”的命令式思维,转向关注“做什么”的声明式思维。