【问题标题】:Why doesn't this simple for loop work as expected?为什么这个简单的 for 循环不能按预期工作?
【发布时间】:2015-11-13 01:29:27
【问题描述】:

您可能希望以下内容打印出abc

var i, rowName;
for (i = 0; i < 3; i++, rowName = ['a', 'b', 'c'][i]) {
    console.log(rowName);
}

但是,它会打印出undefinedbc。为什么?

澄清一下:我知道如何做到这一点;我很好奇的是为什么上面的方法不起作用。

【问题讨论】:

  • 最终表达式仅在每次循环迭代的结束 处计算。 rowName 在此之前未定义。见for @ MDN
  • for 循环中的第三个(和第二个)表达式直到每次迭代结束时才被计算。如果预先评估它以分配rowName,它也会立即将i 增加到1,跳过0 的初始值。
  • 文档再清楚不过了。它说“final-expression 一个要在每次循环迭代结束时评估的表达式。这发生在下一次评估条件之前。通常用于更新或增加计数器变量。”

标签: javascript loops


【解决方案1】:

它打印undefinedbc 的原因是因为for loop 的工作原理。

for (initialization; condition; final expression)

让我们分解你的 for 循环。

初始化i = 0

条件i &lt; 3

最终表达式i++, rowName = ['a', 'b', 'c'][i]

首次进入循环时,i 设置为 0。这是初始化步骤。然后检查条件步骤i &lt; 3。这是在每次迭代之前完成的,以决定是否继续循环。在每个循环之后,评估最终表达式。在您的示例中,您在根据当前索引将 rowName 设置为等于 ['a', 'b', 'c'] 中的元素之前增加 i

在您的情况下,在第一次迭代中,rowNameundefined,因为 最终表达式 尚未计算。此后的每次迭代都按照您的预期进行,因为之前已经对最终表达式进行了求值。

【讨论】:

  • "在您的示例中,在设置 rowName 之前增加 i"。没错,但是增加i 与分配rowName 有什么关系?误导。
  • @PHPglue 这有什么误导性?我没有说任何一个与另一个有任何关系。但是,这两个语句恰好都是“最终表达式”或“循环后操作”,因此在每个循环之后都会进行评估。
  • 这可能会让 OP 误以为这很重要。
  • @PHPglue 思考什么是重要的?
  • 在设置rowName 之前增加i 与for 循环的第三部分在第一遍完成之前不会执行的事实无关。它只是让i 在第二遍以1 开头。
【解决方案2】:

如果你想做棘手的单行 for 循环样式,“正确”的语法是:

var array = ['a', 'b', 'c'];
for (var i = 0, rowName; rowName = array[ i++ ]; ) {
    console.log(rowName);
}

注意for 循环声明的结尾;。从技术上讲,; 后面有一个空语句,这是你通常会做的i++

在这种情况下,for 循环的“条件”利用了 Javascript 赋值运算符。如果你有这样的代码:

var a;
if( a = 1 ) { // Note this is assignment = not comparison ==
    console.log('true');
}

记录“真”。为什么?因为在表达式内部,a = 1 实际上返回 1。而1 是“真实的”,这意味着它在诸如if 语句之类的布尔上下文中评估为true。

如果你的值是假的,反之亦然:

var a;
if( a = 0 ) {
    console.log('true');
}

它不会记录,因为 a = 0 返回 0(以及将 0 分配给 a)。 0 是假的。

这种古怪的 for 循环语法仅适用于某些条件:

  • 如果任何数组元素为“假”(nullundefined"" 等),它将提前终止循环,因为运算符的工作方式如上所述。
  • 这假设您不关心循环索引i。因为i++for 块之前 之前执行,所以循环的每次迭代都会关闭1。也就是说,for 正文第一次执行时,i 将为 1,而不是其声明的起始值 0。
  • 这种模式的唯一好处是节省了几个字节。由于上述两个陷阱,它通常不会在现实世界中使用。

【讨论】:

    【解决方案3】:

    我想你想要这个?

    var i, rowName;
    for (i = 0; i < 3; i++){
        rowName = ['a', 'b', 'c'][i];
        console.log(rowName);
    }
    

    【讨论】:

    • 因为对rowname 的赋值发生在每次执行循环之后。所以第一次通过,rowname 没有被分配,因此你得到undefined。 (您可以将分配移动到 for 循环标头中的“中间”表达式。)
    【解决方案4】:

    您在循环的每一步都重新分配了rowName,它的开头是undefined。您可以执行以下操作:

    for(var i=0,rowName=['a','b','c'],l=rowName.length; i<l; i++){
      console.log(rowName[i]);
    }
    

    或类似的东西:

    var rowName = ['a', 'b', 'c'];
    for(var i=0,l=rowName.length; i<l; i++){
      console.log(rowName[i]);
    }
    

    真正的问题是for(assign; test; execute) 中的第三个条件直到循环的一个test 得到满足才execute。如果test 失败execute 永远不会发生。如果test 传递execute 真的在循环的第一次传递之后开始。

    【讨论】:

    • OP 的 sn-p 正在尝试单独记录每个值。虽然这将记录整个数组,但每次迭代。
    猜你喜欢
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-16
    • 2016-01-10
    相关资源
    最近更新 更多