JavaScript 中未初始化变量的范围

Scope of uninitialized variable in JavaScript

本文关键字:范围 变量 初始化 JavaScript      更新时间:2023-09-26

我不明白为什么这段代码会这样运行:

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);
    }
}

(小提琴)

如果我j设置为null并检查null,它按照我认为的方式工作。

这不是Java,C#,C++等的工作方式,因此令人困惑。

这是因为 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 关键字:

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);
    }
}

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

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

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

ES6 建议添加 let 声明,该声明的范围将限定为最接近的块。 请参阅使用"let"和"var"声明变量有什么区别?有关let的更多信息。

JavaScript 中没有块作用域,只有全局和函数作用域。虽然 JavaScript 变量语句非常灵活,以至于它们会给程序员一种可能存在块作用域的错觉,但事实是,JavaScript 函数中声明的变量稍后由解释器挂起。这意味着它们的声明将移动到最近声明的函数的顶部。举个例子:

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赋值,然后打印您的消息。