经典的 for 循环
当你知道要重复多少次时,for 循环就是合适的工具。它把计数循环的三个部分(起点、终点、步长)都压缩进一行循环头里。
五次迭代,五行输出。来解读一下循环头:
let i = 0在循环开始前运行一次,用来设置计数器。i < 5在每次迭代前检查。如果为true,就执行循环体;如果为false,循环结束。i++在每次迭代后运行,紧接着再次检查条件。
这三个部分用分号隔开,而不是逗号。三者都可以省略,但很少有人这么做,那种情况通常会改用 while。
各部分如何配合
手动追踪一次循环,有助于牢记执行顺序:
一步一步来看:
let i = 1:创建计数器并设为 1。- 检查
i <= 3:为 true,执行循环体,打印1。 - 运行
i++:i现在是 2。 - 检查
i <= 3:为 true,打印2。 - 运行
i++:i现在是 3。 - 检查
i <= 3:为 true,打印3。 - 运行
i++:i现在是 4。 - 检查
i <= 3:为 false,退出。
update 步骤在循环体之后运行,而不是之前。这正是很多人容易搞错的地方。
按索引遍历数组
for 循环最常见的用途就是遍历数组。计数器同时充当索引:
有几点值得注意:
- 数组的索引从 0 开始。第一个元素的索引是
0,最后一个是length - 1。 - 条件是
i < fruits.length,而不是i <= fruits.length。用<=会多跑一次,越过末尾并打印出undefined。 i用let声明,所以它的作用域仅限于循环内部。在循环外面,它并不存在。
如果你只需要值而不需要索引,for...of 更短也更清晰,我们会在单独的文档里讲它。
break:提前停止
break 会立即退出循环。当你已经找到要找的东西、不需要继续时,它就很有用:
break 一执行,控制流就会跳到循环的右花括号之后。update 步骤不会运行,条件也不会再检查,循环直接结束。
continue:跳过本次迭代
continue 会跳过当前迭代的剩余部分,直接进入 update 步骤。循环会继续运行,只是不再完成当前这一轮。
偶数会碰到 continue,从而跳过 console.log,只有奇数会被打印出来。当你想过滤掉某些迭代、又不想把循环体的其余部分嵌套进 if 里时,continue 就很方便。
步长不为 +1 的情况
update 步骤只是一个表达式,不一定非得是 i++。每次加 2:
倒着数:
倒序遍历数组,在边遍历边删除元素时有时很有用:
无论选择哪种写法,规则都一样:条件和 update 必须配合起来,让条件最终变为 false。否则循环会一直运行下去。for (let i = 0; i < 10; i--) 就是一个死循环,因为 i 朝错误的方向变化。
嵌套 for 循环
你可以把一个 for 循环放进另一个 for 循环里。外层循环的每一次迭代,内层循环都会完整地运行一遍。
九行输出:外层迭代三次,每次内层迭代三次。给每个计数器起一个有意义的名字(row/col、i/j),不要重复使用同一个变量。
有一点要注意:break 和 continue 只作用于最内层的循环。跳出内层循环并不会停止外层循环。如果需要那样做,可以设置一个标志变量并在外层循环中检查它,或者把嵌套的逻辑提取成一个函数,然后在其中 return。
常见陷阱
几个初学者常踩的坑:
差一错误。 i <= arr.length 会多迭代一次;i < arr.length - 1 会少迭代一次。标准写法是 i < arr.length。
忘记写 update。 如果漏掉了 i++(或类似的语句),计数器永远不变,循环就会一直运行下去:
for (let i = 0; i < 10; ) {
console.log(i); // never ends
}
用 var 声明计数器。 var 是函数作用域的,所以计数器会泄漏到循环之外,在闭包中引发意想不到的问题。坚持使用 let。
在遍历数组时修改它。 删除元素会让索引移位,导致你跳过某些元素。如果需要删除元素,就倒序遍历,或者用 filter 构建一个新数组。
何时改用其他写法
经典的 for 循环随时可用,但对于常见场景,JavaScript 提供了更简短的选择:
- 遍历数组的值:
for (const item of array)更简洁。 - 转换数组:
array.map(fn)返回一个新数组。 - 筛选:
array.filter(fn)。 - 求和或归约:
array.reduce(fn, start)。 - 只是对每个元素做点什么:
array.forEach(fn)。
当你确实需要索引、需要中途跳过或退出,或者需要倒数、每次加 2 这类非标准步长时,再使用经典的 for 循环。
下一步:while 循环
当你事先知道范围时,for 循环最好用。如果不知道,比如想一直运行到某个条件改变为止,while 和 do...while 会更合适。这就是下一页的内容。
常见问题
JavaScript 中 for 循环的语法是什么?
括号里有三个部分,用分号隔开:for (init; condition; update) { ... }。init 只运行一次,condition 在每次迭代前检查,update 在每次迭代后运行。典型的写法是 for (let i = 0; i < 10; i++) { ... }。
JavaScript 中如何遍历数组?
用带索引的经典 for 循环就行:for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }。如果不需要索引,for...of 更简洁:for (const item of arr) { ... }。要转换或筛选数组,通常用 map 和 filter 这类数组方法更合适。
for 循环中的 break 和 continue 是怎样工作的?
break 会立即退出循环,执行跳到循环之后的代码。continue 会跳过当前迭代的剩余部分,直接进入 update 步骤,然后再次检查条件。除非使用标签,否则两者都只作用于最内层的循环。
为什么我的 for 循环停不下来?
通常是 update 步骤从未让条件向 false 靠近。for (let i = 0; i < 10; i--) 会无限循环,因为 i 从 0 开始,只会越来越小。仔细检查条件和 update,它们必须配合起来,让条件最终变为 false。