Skip to content

循环

循环 是一种重复运行同一代码的方法。

while

while 循环的语法如下:

js
while (condition) {
  // code,所谓的循环体
}

condition 为真时,执行循环体的 code

提示

如果循环体只有一条语句,则可以省略大括号 {…}

js
let i = 3
while (i) alert(i--)

do...while

使用 do...while 语法可以将条件检查移至循环体下面,语法如下:

js
do {
  // code,所谓的循环体
} while (condition)

提示

这种形式的语法很少使用,除非你希望不管条件是否为真,循环体至少执行一次。

for

for 循环语法如下:

js
for (begin; condition; step) {
  // code,循环体
}

提示

for 循环虽然更加复杂,但它是最常使用的循环形式。

"内联"变量声明

这里“计数”变量 i 是在循环中声明的。这叫做“内联”变量声明。这样的变量只在循环中可见。

js
for (let i = 0; i < 3; i++) {
  alert(i) // 0, 1, 2
}
alert(i) // 错误,没有这个变量。

除了定义一个变量,我们也可以使用现有的变量:

js
let i = 0

for (i = 0; i < 3; i++) {
  // 使用现有的变量
  alert(i) // 0, 1, 2
}

alert(i) //3,可见,因为是在循环之外声明的

省略语句段

for 循环的任何语句段都可以被省略。

例如,如果在循环开始时不需要做任何事,就可以省略 begin 语句段:

js
let i = 0
// 省略 begin 语句段
for (; i < 3; i++) {
  alert(i) // 0, 1, 2
}

也可以省略 step 语句段:

js
let i = 0
// 省略 step 语句段
for (; i < 3; ) {
  alert(i++)
}

该循环与 while (i < 3) 等价。

可以省略所有内容,从而创建一个无限循环:

js
// 省略所有内容
for (;;) {
  // 无限循环
}

请注意 for 的两个 ; 必须存在,否则会出现语法错误。

阻断循环

break

break 用于打断循环。

例如,下面这个循环要求用户输入一系列数字,在输入的内容不是数字时“终止”循环。

js
let sum = 0

while (true) {
  let value = +prompt('Enter a number', '')
  if (!value) break // (*)
  sum += value
}

alert('Sum: ' + sum)

continue

continue 用于继续下一次迭代。

下面这个循环使用 continue 来只输出奇数:

js
for (let i = 0; i < 10; i++) {
  //如果为真,跳过循环体的剩余部分。
  if (i % 2 == 0) continue

  alert(i) // 1,然后 3,5,7,9
}

continue 指令利于减少嵌套

显示奇数的循环可以像下面这样:

js
for (let i = 0; i < 10; i++) {
  if (i % 2) {
    alert(i)
  }
}

从技术角度看,它与上一个示例完全相同。当然,我们可以将代码包装在 if 块而不使用 continue。

但在副作用方面,它多创建了一层嵌套(大括号内的 alert 调用)。如果 if 中代码有多行,则可能会降低代码整体的可读性。

禁止 break/continue? 的右边

不要这样:

js
if (i > 5) {
  alert(i);
} else {
  continue; // ❌
}

亦或者这样:

js
(i > 5) ? alert(i) : continue; // ❌

JSLabel

有时候我们需要一次从多层嵌套的循环中跳出来。

我们可以通过 JSLabel,语法如下:

js
labelName: for (...) {
  ...
}

例如:使用 break labelName 语句跳出循环至标签处:

js
outer: for (let i = 0; i < 10; i++) {
  console.log('顶层循环')
  for (let j = 0; j < 10; j++) {
    console.log('内层循环', i, j)
    if (i * j > 30) {
      console.log('退出顶层循环')
      break outer
    }
  }
}

还可以将标签移至单独一行:

js
outer:
for (let i = 0; i < 10; i++) { ... }

continue 指令也可以与标签一起使用。在这种情况下,执行跳转到标记循环的下一次迭代。

for...in

用于遍历对象的属性。

js
let obj = {
  name: 'John',
  age: 30,
}

for (let prop in obj) {
  console.log(prop)
}

语法

  • for 关键字
  • 迭代变量:用于存储对象的每个属性
  • 对象:要遍历的对象

示例

js
let obj = {
  name: 'John',
  age: 30,
}

for (let prop in obj) {
  console.log(prop)
}

该代码将输出以下内容:

name
age

for...of

用于遍历可迭代对象,例如数组、字符串和集合。

js
let arr = [1, 2, 3]

for (let item of arr) {
  console.log(item)
}

语法

  • for 关键字
  • 迭代变量:用于存储可迭代对象的每个值
  • 可迭代对象:要遍历的对象,例如数组、字符串和集合

示例

js
let arr = [1, 2, 3]

for (let item of arr) {
  console.log(item)
}

该代码将输出以下内容:

1
2
3

iterables 迭代器

[].forEach()

为啥 await 不能用在 forEach 中?

例如:

js
function test() {
  let arr = [1, 2, 3]
  arr.forEach(async item => {
    const res = await fetch(item)
    console.log(res)
  })
  console.log('end')
}

function fetch(x) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(x)
    }, 500 * x)
  })
}

test()

期望的打印结果是:

3
2
1
end

而实际打印顺序是:

end
1
2
3

其原因就是 forEach 只支持同步代码,可以参考下 Polyfill 版本的 forEach:

js
while (index < arr.length) {
  callback(item, index) //也就是我们传入的回调函数
}

要想在循环中使用 async / await,请使用 for...of 或者 for 循环while 循环,可以这样写:

js
async function test() {
  let arr = [1, 2, 3]

  for (const item of arr) {
    const res = await fetch(item)
    console.log(res)
  }
  console.log('end')
}

function fetch(x) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(x)
    }, 500 * x)
  })
}

test()

基于 MIT 许可发布