在JavaScript的for循环声明中声明变量

11

我相信在SO上看到过这方面的讨论,但现在无法找到。简单来说,在循环语句中声明增量是否存在缺点?下面两种方式有什么区别:

function foo() {
    for (var i=0; i<7; i++) {
        // code
    }
}

...还有这个:

function foo() {
    var i;
    for (i=0; i<7; i++) {
        // code
    }
}

因为JS有函数作用域,所以两种方式都可以,对吗?但是是否存在边界情况,前一种方法会导致问题?

如果它们相同,那么为什么Crockford/JSLint完全不允许呢?


2
可能是JavaScript变量在循环内外声明的问题?的重复。 - K Scandrett
5个回答

14
这两者完全相同。JavaScript中的所有局部变量都具有函数作用域,这意味着它们在声明它们的整个函数中都是有效的。这通常会让人感到反直觉,因为大多数花括号语言将变量的生命周期限定在它们声明的块中。
一部分JavaScript开发者非常喜欢第二种形式。理由是,由于所有变量都具有函数作用域,您应该在函数级别上声明它们,以使即使对JavaScript不熟悉的人也能明确其生命周期。但这只是一种风格,绝不是硬性规定。
编辑:请注意,随着ES6 let的引入,您现在可以在循环中使用let来实现真正的块级作用域变量更多详情
for(let i = 1; i <= 5; i++) {
   setTimeout(function(){
       console.log('Value of i : ' + i);
   },100);
}

还有一个 let,它类似于 var,也提供了本地作用域,但不同的是它接收块级作用域。它在 for/if/while 等之外是不可访问的。 - 4castle

8
在循环头部使用var声明的问题在于它具有欺骗性。看起来好像你正在声明一个作用域仅限于for循环的变量,但实际上它存在于函数中的任何位置 - 包括在声明之前。
var i = 1;
function foo() {
   console.log(i);                     // 'undefined'
   for (var i=1; i<100; ++i) {
   }
}

尽管 console.log 调用发生在本地变量 i 的声明之前,但由于它在同一个函数内,因此仍然适用于它。因此,本地变量 i 尚未分配任何值,这就是传递给 log 的内容。这可能会让人感到惊讶;对于不熟悉 Javascript 作用域规则的人来说,这显然并不明显。
ECMAScript 2015 开始,有一种更好的方式来声明变量:let。使用 let 声明的变量仅局限于包含它们的块,而不是整个函数。因此,上述代码的这个版本将按预期打印出 1
let i=1; // could use var here; no practical difference at outermost scope
function foo() {
  console.log(i);               // 1
  for (let i=1; i<100; ++i) {
  }
}

现代JavaScript的最佳实践是使用let而不是var来声明变量。但是,如果你被困在ECMAScript 2015之前的实现中,最好在函数顶部声明所有变量,而不是等到第一次使用时再声明。

只要你熟悉Js对作用域的处理,那么一切都很好。 - Benjamin Allison
1
这就是为什么人们必须停止使用“var”的原因。 - Sartheris Stormhammer
感谢提醒,@SartherisStormhammer。已更新以解决现代语言中 let 的可用性问题。 - Mark Reed

1

这两种方式没有区别,但我更喜欢第二种方式(Crockford 推荐的方式),因为它明确地显示了变量在 for 循环外可用:

function() {
    for(var i=0; i<7; i++) {
        // code
    }

    // i is still in scope here and has value 7
}

0

这两个东西完全一样。


这并不是一个非常有帮助的答案;问题是,既然它们在功能上是等效的,为什么JSLint要有这个规则呢?JSLint对各种事情都异常挑剔,它们对你的重要性可能会有很大的差异,但是它的疯狂背后有一定的方法。 - Mark Reed
1
笑,它意味着一些东西...也许不是很重要,但对于像我这样的新手来说,它有助于建立良好的习惯。 - Benjamin Allison
@Rob:如果你不想回答,可以忽略它,但是OP特别询问为什么会出现这种行为。你对软件的鄙视并不意味着他们不应该得到问题的答案。 - Mark Reed
1
JSLint可能会有所帮助,但它过于主观,并且太多人似乎认为如果您无法通过jslint验证,则您的代码就很糟糕。 - Rob

0

这两个代码块是相同的。在 for 循环开始之前,将执行循环的第一条语句,当第二条语句为真时循环运行,每次循环迭代时都会运行第三条语句。

这意味着

for(var i = 0; i < 8; i++) {
    //some Code
}

等同于

var i = 0;
for(;i < 8;) {
    //some Code
    i++;
}

(在(后面的分号是告诉计算机i < 8实际上是第二个语句,而不是第一个语句。)


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