【问题标题】:JavaScript Lexical Scoping and Life of VariableJavaScript 词法作用域和变量的生命周期
【发布时间】:2011-12-29 15:21:14
【问题描述】:

我很好奇为什么会这样:

function doThis(){
    counter = 0;
    return counter;
};

console.log(counter); // returns "reference error: can't find variable"

这是有道理的,因为变量不存在于函数之外。但是,如果我创建一个自己执行的函数:

(function doThis(){
    counter = 0;
    return counter;
})();

console.log(counter); // returns 0

变量counter怎么还存在?不是闭包,好像没有从外部引用这个变量,所以不应该被垃圾回收销毁吗?

【问题讨论】:

  • 未声明的变量隐式成为全局变量......所以在你的第二个例子中,函数执行后,counter 是一个全局变量......
  • 请注意,在严格模式下,对未声明变量的赋值会引发引用错误。因此,您应该始终事先声明您的变量。

标签: javascript function closures scope


【解决方案1】:

您将其创建为全局变量,因为您没有在变量名前包含 var

第一个示例中的函数尚未被调用,因此尚未创建变量,在第二个示例中它已创建,这就是为什么您会得到 0

你的代码应该做的是:

function doThis(){
    var counter = 0;
    return counter;
};

【讨论】:

    【解决方案2】:

    首先编辑它们,以便立即清楚发生了什么(不遗漏 var hack):

    function doThis(){
        window.counter = 0;
        return counter;
    };
    
    console.log(window.counter); // returns undefind
    

    还有:

    (function doThis(){
        window.counter = 0;
        return counter;
    })();
    
    console.log(window.counter); // returns 0
    

    你现在能看到发生了什么吗?该函数定义了一个全局变量,因此在调用该函数之前它当然不可用。 window 指的是浏览器中的[object global]

    这就是您总是希望使用global.somethingvar something 的原因,因此任何人都非常清楚您打算使用全局变量还是局部变量。如果您在 OP 中使用 var,则该变量将是本地变量。

    【讨论】:

      【解决方案3】:

      由于您没有使用“var”声明它,因此它被分配给在函数外部可见的全局范围。在第一个示例中,您没有执行该函数,因此从未定义计数器,而在第二个示例中,您调用该函数并将计数器分配给全局范围

      【讨论】:

        【解决方案4】:

        在第一个示例中,您还没有调用函数,因此 counter 还不存在(因为函数内部的代码尚未执行)。在第二个示例中,您已经定义了一个函数字面量并且您正在自调用它。函数内部的代码 execute 和 counter 现已定义。

        此外,counter 是一个全局变量,因为您没有使用 var 定义它,因此它对函数外部的范围可见。和window.counter = 0一样。

        现在,如果您执行了以下操作:

        (function doThis(){
            var counter = 0; //notice the var
            return counter;
        })();
        

        counter 仍然是未定义的,因为它在函数范围内是本地的。

        回顾一下:

        • 在第一个示例中,counter 未定义,因为代码尚未运行。如果您实际调用该函数,您将获得与第二个示例相同的行为。
        • 在第二个示例中,counter 被定义并且是一个全局变量(与window.counter 基本相同),它的定义是因为函数内部的代码在您定义它并自调用时执行。李>
        • 在第三个示例中,counter 在全局范围内是未知的,因为它在定义它的函数中是本地的(因为使用了 var)。

        【讨论】:

          【解决方案5】:

          如你所知,在 JavaScript 中,如果你声明一个没有“var”关键字的变量,它将被添加到全局范围(窗口对象)中。

          但是,如果在函数中声明变量时没有使用“var”关键字,则在调用该特定函数之前,它不会被添加到全局范围(窗口对象)中。 在 JavaScript 中,您必须了解执行上下文的创建。

          解释完整的JS文件时,为所有函数和变量分配内存(提升)。 请注意,所有在函数之外声明的变量都会添加到全局范围(窗口对象)中。

          执行阶段:

          由于 JS 代码执行是同步的(一次只有一行)和单线程,因此会创建一个执行堆栈,并将“全局执行上下文”推送到执行堆栈。 如果在执行中遇到函数调用,则为对应的函数创建“函数执行上下文”并推送到同一个栈中。

          现在在函数执行期间,JS 引擎解析代码,如果遇到变量,则分配内存,如果变量声明时没有“var”关键字,它会将这个特定变量添加到全局范围。否则,该特定变量将成为函数范围(本地范围)的一部分。

          现在让我们检查您的代码 sn-ps:

          function doThis(){
              counter = 0;
              return counter;
          };
          

          console.log(计数器); // 返回“引用错误:找不到变量”

          在这个例子中,由于 doThis() 函数从不执行,变量“counter”没有分配任何内存,也不是任何范围(全局/局部)的一部分。因此在控制台日志中,您会看到一个参考错误。

          (function doThis(){
              counter = 0;
              return counter;
          })();
          

          console.log(计数器); // 返回 0

          作为一个自调用函数,“计数器”变量将被分配内存并移动到全局范围(窗口对象)。因此您可以在控制台日志中看到“计数器”变量的值。

          【讨论】:

            猜你喜欢
            • 2011-07-15
            • 2011-08-06
            • 2013-08-12
            • 2018-12-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-12-20
            相关资源
            最近更新 更多