JavaScript中未初始化变量的范围

时间:2015-01-02 04:05:54

标签: javascript

我无法理解为什么这段代码表现得像:

for (var i = 1; i < 3; i++) {
    var j;
    if (!j) {
        j = 1;
    } else {
        alert("why are we here? j shouldn't be defined but it's " + j);
    }
}

jsFiddle

如果我将j设置为null并检查null,则会按照我认为的方式运作。

这不是Java,C#,C ++等的工作方式,因而是混乱。

3 个答案:

答案 0 :(得分:10)

这是因为JavaScript中的变量范围限定为函数(如果不在函数内,则为全局范围)。 var不在循环内部,并且花括号不定义新的闭包。基本上,j的值在循环体后面仍然存在,var不会将j重新定义为undefined。这就是明确设置var j = null;具有预期效果的原因。

示例1:

考虑到这一点的一个好方法是,只要你用var声明变量,就像这样。

function someFunc() {
    for(var i = 0; i < 3; i++){
        var j;
    }
}

解释器提升变量声明,如此。

function someFunc() {
    var i;
    var j;
    for(i = 0; i < 3; i++){
    }
}

请注意,由于var j声明被提升到函数的顶部,声明实际上在循环中什么也没做。

示例2:

但是,如果您要使用null这样初始化变量。

function someFunc() {
    for(var i = 0; i < 3; i++){
        var j = null;
    }
}

它将被解释为这样。

function someFunc() {
    var i;
    var j;
    for(i = 0; i < 3; i++){
        j = null;
    }
}

注意每个循环如何j设置为null

ES6 let keyword:

ES6中有一个关键字会在这样的循环中创建一个范围,它是let关键字。请注意,此时let关键字的浏览器支持效果不佳。

for (var i = 1; i < 3; i++) {
    let j;
    if (!j) {
        j = 1;
    } else {
        alert("why are we here? j shouldn't be defined but it's "+ j);
    }
}

答案 1 :(得分:4)

第一次for循环执行循环体时,j未定义,因此您的代码设置为j=1。在循环的后续迭代中,j已经定义并设置为1,因此它会按照预期进入else子句。

这是因为在Javascript中使用var定义的变量是函数作用域,而不是块作用域,如果不在函数内,则它们是全局的。因此,jsFiddle中只有一个变量jfor循环的每次迭代都使用相同的变量(从而继承上一次迭代的值)。

如果您在j = null;循环体内初始化for,它将起作用,因为那时您将为每次迭代重新初始化它,而不是使用上一次迭代中的值。

ES6建议添加范围为最近的块的let声明。有关let的详细信息,请参阅What's the difference between using "let" and "var" to declare a variable?

答案 2 :(得分:2)

JavaScript中没有块范围,只有global and function scopes。虽然,JavaScript variable statements非常灵活,它们会给程序员一个块范围可能存在的假象,但事实是JavaScript函数中声明的变量后来由解释器hoisted。这意味着它们的声明被移动到最近声明的函数的顶部。以此为例:

function test() {
   console.log('a test');
   for (var i = 0; i < 100; i++) {
      var k = i + Math.random();
      console.log(k)
   }
}

JavaScript解释器在内部将代码“转换”为以下内容:

function test() {
   var i, k; // var declarations are now here!

   console.log('a test');
   for (i = 0; i < 100; i++) {
      k = i + Math.random();
      console.log(k)
   }
}

它会将所有var声明移动到最近的函数声明的开头。在您的代码中,提升将创建以下代码:

// A anonymous function created by jsFiddle to run your script
function () {
   var i, j;

   for (i = 1; i < 3; i++) {
      if (!j) {
         j = 1;
      } else {
         alert("Why are we here? j shouldn't be defined, but it's " + j);
      }
   }
}

第一次变量为undefined,然后分配1,然后打印出您的信息。