主题
循环
循环 是一种重复运行同一代码的方法。
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()