跳到主要内容

循环

循环

循环体的单次执行叫作 一次迭代。

for 循环

for (begin; condition; step) {
// ……循环体……
}

// 变量 i 是在循环中声明的,这叫做“内联”变量声明
for (let i = 0; i < 3; i++) { // 结果为 0、1、2
alert(i);
}
  • for 循环的任何语句段都可以被省略。
let i = 0; // 我们已经声明了 i 并对它进行了赋值

for (; i < 3; i++) { // 不再需要 "begin" 语句段
alert( i ); // 0, 1, 2
}
for (; i < 3;) {
alert( i++ );
}
for (;;) {
// 无限循环
}

for..of

for..of 语句在可迭代对象(包括 ArrayMapSetStringTypedArrayarguments,DOM 元素集合(比如一个NodeList对象) 等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

info

一些内置类型拥有默认的迭代器行为,其他类型(如 Object)则没有。拥有默认的 @@iterator 方法的内置类型是:

  • Array.prototype[@@iterator]()
  • TypedArray.prototype[@@iterator]()
  • String.prototype[@@iterator]()
  • Map.prototype[@@iterator]()
  • Set.prototype[@@iterator]()
let fruits = ["Apple", "Orange", "Plum"];

// 遍历数组元素
for (let fruit of fruits) {
alert( fruit );
}
tip

对于for..of的循环,可以由 break, throwreturn 终止。

for..ofasync..await结合使用:

async function fn(category1List) {
for (const ele of category1List) {
if (category1CodeList.includes(ele.categoryCode)) ele.children = await this.getNextLevelList(2, ele.categoryCode)
if (Object.prototype.toString.call(ele.children) === '[object Array]') { // 判断类型,避免对undefined进行迭代报错:undefined is not iterable (cannot read property symbol(symbol.iterator)) at _iterabletoarray
for (const childrenEle of ele.children) {
if (category2CodeList.includes(childrenEle.categoryCode)) childrenEle.children = await this.getNextLevelList(3, childrenEle.categoryCode)
}
}
}
}

for..in

  • 为了遍历一个对象的所有键(key),可以使用一个特殊形式的循环:for..in。注意,所有的 “for” 结构体都允许我们在循环中定义变量,像如下的 let prop

  • 遍历一个对象时,整数属性会被进行排序,其他属性则按照创建的顺序显示。

    这里的“整数属性”指的是一个可以在不做任何更改的情况下与一个整数进行相互转换的字符串。例如:"49" 是一个整数属性名(因为我们把它转换成整数,再转换回来,它还是一样的。),但是 “+49” 和 “1.2” 就不是。

  • 技术上来讲,因为数组也是对象,所以使用 for..in 也是可以的,但是会有一些潜在问题存在:

    1. for..in循环会遍历所有属性(不仅仅是数组的数字属性),如果使用for..in遍历类数组对象(类数组对象看似是数组,也就是说,它们有 length 和索引属性,但是也可能有其它的非数字的属性和方法),非数字的属性也会被遍历;

    2. for..in 循环适用于普通对象,并且做了对应的优化。但是不适用于数组,因此速度要慢 10-100 倍(当然即使是这样也依然非常快,只有在遇到瓶颈时可能会有问题)。通常来说,我们不应该用 for..in 来处理数组。

使用 break 指令跳出循环

let sum = 0;

while (true) {

let value = +prompt("Enter a number", '');

if (!value) break; // 执行后立刻终止循环,将控制权传递给循环后的第一行,即alert( 'Sum: ' + sum );

sum += value;

}
alert( 'Sum: ' + sum );

使用 continue 指令继续下一次迭代

continue 不会停掉整个循环,而是停止当前这一次迭代,并强制启动新一轮循环(如果条件允许的话)。

break/continue 标签

  • 用于一次从多层嵌套的循环中跳出来或跳转到标记循环的下一次迭代

  • 标签 是在循环之前带有冒号的标识符

    labelName: for (...) {
    ...
    }
  • break <labelName> 语句跳出循环至标签处

    outer: for (let i = 0; i < 3; i++) {

    for (let j = 0; j < 3; j++) {

    let input = prompt(`Value at coords (${i},${j})`, '');

    // 如果是空字符串或被取消,则中断并跳出这两个循环。
    if (!input) break outer; // 执行后 向上寻找名为 outer 的标签并跳出当前循环,控制权转至 alert('Done!')。

    // 用得到的值做些事……
    }
    }

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

switch 语句

switch(x) {
case 'value1': // if (x === 'value1')
...
[break]

case 'value2': // if (x === 'value2')
...
[break]

default:
...
[break]
}
  • 比较 x 值与第一个 case(也就是 value1是否严格相等(被比较的值必须是相同的类型才能进行匹配),然后比较第二个 casevalue2)以此类推。

  • 如果相等,switch 语句就执行相应 case 下的代码块,直到遇到最靠近的 break 语句(或者直到 switch 语句末尾)。

  • 如果没有符合的 case,则执行 default 代码块(如果 default 存在)。

  • 如果没有 break,程序将不经过任何检查就会继续执行下一个 case

  • switchcase 都允许任意表达式。

  • 共享同一段代码的几个 case 分支可以被分为一组