while

while 语句创建一个循环,只要测试条件求值为真,则循环执行指定语句。在执行语句前会先对条件进行求值。

尝试一下

语法

js
while (condition)
  statement
condition

每次循环之前求值的表达式。如果求值为真statement 就会被执行。如果求值为假,则执行 while 循环后面的语句。

statement

可选语句,只要条件表达式求值为真就会执行。要在循环中执行多条语句,可以使用语句({ /* ... */ })包住这些语句。

注意:使用 break 语句在 condition 求值为真之前停止循环。

示例

使用

只要 n 小于三,下面的 while 就会一直迭代。

js
let n = 0;
let x = 0;

while (n < 3) {
  n++;
  x += n;
}

在每次迭代中,n 都会自增,然后再把 n 加到 x 上。因此,在每轮循环结束后,xn 的值分别是:

  • 第一轮后:n = 1,x = 1
  • 第二轮后:n = 2,x = 3
  • 第三轮后:n = 3,x = 6

当完成第三轮循环后,条件 n < 3 不再为真,因此循环终止。

使用赋值作为条件

在某些情况下,使用赋值作为条件是有意义的。但这会牺牲可读性,因此有一些样式建议可以让这种模式对所有人都更加清晰。

考虑下面的示例,它遍历文档的评论,并将它们打印到控制台中。

js
const iterator = document.createNodeIterator(document, NodeFilter.SHOW_COMMENT);
let currentNode;
while (currentNode = iterator.nextNode()) {
  console.log(currentNode.textContent.trim());
}

这个例子并不完全符合最佳实践,特别是由于下面这一行代码:

js
while (currentNode = iterator.nextNode()) {

这行代码的效果是可以的——每当找到一个评论节点时:

  1. iterator.nextNode() 返回一个评论节点,并将其赋值给 currentNode
  2. 因此 currentNode = iterator.nextNode() 的值为真值
  3. 因此 console.log() 调用执行,循环继续。

然后,当文档中不再有注释节点时:

  1. iterator.nextNode() 返回 null
  2. 因此 currentNode = iterator.nextNode() 的值为 null,这也是假值
  3. 因此循环终止。

这行代码的问题在于:条件通常使用比较运算符,比如 ===,但是这行代码中的 = 不是比较运算符,而是一个赋值运算符。所以,这个 = 看起来像=== 的拼写错误,尽管实际上它并不是拼写错误。

因此,在这种情况下,一些代码检查工具(如 ESLint 的 no-cond-assign 规则)会报告警告,以帮助你捕捉可能的拼写错误,以便你可以进行修正。警告可能会类似于以下内容:

Expected a conditional expression and instead saw an assignment.

许多样式指南建议更明确地表示条件是一个赋值的意图。你可以通过在赋值周围加上额外的括号作为分组运算符来做到这一点,以最小化地表示意图:

js
const iterator = document.createNodeIterator(document, NodeFilter.SHOW_COMMENT);
let currentNode;
while ((currentNode = iterator.nextNode())) {
  console.log(currentNode.textContent.trim());
}

实际上,这也是 ESLint 的 no-cond-assign 默认配置和 Prettier 强制执行的样式,因此你可能会在实际代码中看到这种模式的很多实例。

有些人可能进一步建议添加比较运算符以将条件转变为显式比较:

js
while ((currentNode = iterator.nextNode()) !== null) {

还有其他方法可以编写此模式,例如:

js
while ((currentNode = iterator.nextNode()) && currentNode) {

或者,完全放弃使用 while 循环的想法:

js
const iterator = document.createNodeIterator(document, NodeFilter.SHOW_COMMENT);
for (
  let currentNode = iterator.nextNode();
  currentNode;
  currentNode = iterator.nextNode()
) {
  console.log(currentNode.textContent.trim());
}

如果读者对将赋值用作条件的模式非常熟悉,那么所有这些变体的可读性应该是相等的。否则,最后一种形式可能是最易读的,尽管它是最冗长的。

规范

Specification
ECMAScript Language Specification
# sec-while-statement

浏览器兼容性

BCD tables only load in the browser

参见