更好地理解JavaScript循环

9

在阅读JS编辑器(Tern)中的代码时,我遇到了以下代码片段中使用for循环的各种用法:

代码片段1 @ 第463-468行

for (;;) {
  /* some code */
}

代码片段 2 @ 第 97-100 行

for (var i = 0; ; ++i) {
  /* some code */
}

同样地,我也遇到了一个空循环体的for循环,例如:

for (var p; p; p = someValue) /* empty body */ ;

我正在尝试理解代码执行流程

我的理解是,对于片段1中的代码,for循环没有条件,因此它可能会无限继续吗?对于片段2中的代码,i不断增加而没有限制吗?对于第三个代码片段,循环将继续直到p被分配为评估为false的内容为止?

这些是我脑海中的想法,但我不确定。请协助。


4
没问题,你说得对。如果这两个循环体中有break语句,它们就可以提前结束。 - piokuc
2个回答

5

简而言之

首先,你的所有断言都是正确的。

  • 第一个循环一直运行,直到它被突然中止(使用 break、return、throw 等)。
  • 第二个循环一直运行,直到它被突然中止,并执行变量赋值和增量操作。
  • 第三个 for 循环像普通的 for 循环一样运行,直到中间条件为 falsey。它的主体为空。

但为什么JavaScript会这样做?

让我们深入探讨一下为什么会发生这种情况。

如果我们仔细查看语言规范,我们可以看到在for循环中发生了以下情况:

IterationStatement:for(ExpressionNoIn(opt);Expression(opt);Expression(opt)) Statement

我将在本答案的其余部分中处理这些语句和定义。

现在让我们逐个讨论这些情况。

对于for(;;)的第一种情况,会发生以下情况:

  • ExpressionNoIn不存在,因此不会为该子句调用任何内容(如条款1所述)。

  • 第二个表达式不是in,因此我们不会返回调用(如条款3所述)。

  • 第三个表达式为空,因此不会执行“增量”(如条款3.f所述)。

所以它基本上会像你预测的那样无限重复(直到被使用break打破、返回或抛出异常,或者任何导致突然完成的情况)。 (如e和d条款所述)。
在第二种情况下for (var i = 0; ; ++i)发生以下情况:
  • ExpressionNoIn存在,因此我们对其进行评估,并使用getValue分配它(如条款1所述)。 我们不进行分配。

  • 然后我们无限重复,因为第二个表达式不在这里。 因此,我们继续执行,直到发生abrubt执行或中断。 更具体地说,在这里定义。

  • 我们按条款f在每次迭代时递增i

在第三种情况下for (var p; p; p = someValue) /* empty body */ ;发生以下情况:
这是一个for循环。虽然语句为空,但for循环并不在意。唯一的区别是for循环没有返回值。基本上这是一个完整合法的for循环。分号只是一个空语句。当你想运行一个没有实际内容的for循环时,它非常有用。有时你会在特性检测中看到这种情况。当你想找到最小的n时,它也很有用...。
你说得对,它会一直运行直到值为falsey,或者更准确地说,在其上调用ToBoolean会产生false,正如3.a.ii条款所规定的那样。
正如你所看到的,这一切都在规范中得到了明确定义 :)

为什么程序员这样做?

在第一个代码片段中,他们使用break子句执行流程控制。if (eol >= pos || eol < 0) break;(他们检查行末,这可以在更传统的for循环中完成)。

在第二个代码片段中,他们再次使用break进行流程控制:

 if (!definitions.hasOwnProperty(uniq)) { name = uniq; break; }

他们再次将其放在for循环内的break语句中。
第三个代码片段没有上下文,但我们假设我们想要(平凡的例子)找到第一个大于10的数字(或第10个div元素,或字符串的出现-你懂的)。我们可以这样做:
for(var i=0;i<=10;i++);
然后获得第一个大于10的数字。

1
为什么不使用 while (true) 呢? - Nate-Wilkins
例如,在代码高尔夫中,for(;;)更短:P 说实话,没有使用for(;;)的好理由。我解释了它为什么会这样行事,而不是为什么要在代码中使用它。 - Benjamin Gruenbaum

1

你的理解是正确的。

第一个代码片段是一个无限循环;没有终止条件,因此循环将会永远进行下去。几乎肯定伴随着循环体内的break语句,它在运行时退出循环。(另一种选择是抛出异常退出循环,尽管使用异常作为控制流程是不好的做法。)这种模式(或等效的while (true) {...})通常发生在退出条件太复杂而不能在循环语句中表达的情况下。

第二个代码片段与第一个类似,没有终止条件,因此它将永远运行。这也需要break语句(或异常)来终止循环。唯一的区别是在每次迭代中还会递增计数器变量。

第三个代码片段是一个没有循环体的循环,尽管测试和变量更新在每次迭代中都发生。正如你预期的那样,这将继续进行,直到p评估为false。这个for循环与while循环版本完全相同:

var p;
while (p) {
    p = someValue;
}

也许更清晰的是,该任务会重复进行,直到 !p

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接