【问题标题】:How do JavaScript variables work?JavaScript 变量是如何工作的?
【发布时间】:2011-12-16 05:03:28
【问题描述】:

我知道 JavaScript vars 指向一个值:

var foo = true;
//... later 
foo = false;

所以在那个例子中我改变了foo指向true -> foo指向false,但如果我这样做:

for (var i=0; i<100; i++){
    var someVar = i;
}

我是否为每次迭代创建一个新变量?

以下两种做同样的方式有区别吗?

var myvar;
for (var i=0; i<100; i++){
    myvar = i;
}

for (var i=0; i<100; i++){
    var myvar = i;
}

如果是,为什么?

【问题讨论】:

    标签: javascript variables scope var


    【解决方案1】:

    Javascript ES5 及更早版本没有块作用域,只有函数作用域。此外,在函数范围内声明的所有 javascript 变量的声明都会自动“提升”到函数顶部。

    因此,在循环中声明一个变量与在函数顶部声明它然后在循环中引用它并没有什么不同。

    请参阅这两个参考资料以获得一些有用的解释:http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoistinghttp://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/

    注意:对变量的赋值不会被提升,只是变量的声明。所以,如果你这样做:

    function a() {
        for (var i=0; i<100; i++){
            var myvar = i;
        }
    }
    

    它是这样工作的:

    function a() {
        var myvar;
        for (var i=0; i<100; i++){
            myvar = i;
        }
    }
    

    如果您想在 for 循环中创建一个新范围,您可以像这样使用 IIFE(立即调用的函数表达式):

    function a() {
        for (var i=0; i<100; i++){
            (function() {
                var myvar = i;
                // myvar is now a separate variable for each time through the for loop
            })();
        }
    }
    

    2015 年更新。 ES6(或有时称为 ES2015)提供了确实提供块范围的 let 声明。在这种情况下,let 变量声明仅提升到当前块作用域的顶部。截至 2015 年中期,这尚未在浏览器中广泛实施,但即将推出,并且可以在 node.js 等服务器端环境中使用或通过转译器使用。

    所以,在 ES6 中,如果你这样做:

    for (let i=0; i<100; i++){
        let someVar = i;
    }
    

    isomeVar 都将仅在循环本地。

    【讨论】:

      【解决方案2】:

      正如@icktoofay 所说,在 javascript 中没有区别。在某些语言中,在每次迭代时,变量都会被实例化,然后超出范围,然后被垃圾回收。

      要在 javascript 中模拟这个,你可以这样做:

      for (var i=0; i<100; i++){
          (function(){
              var myvar = i;
          })();
      }
      

      在这种情况下,myvar 只存在于匿名函数的范围内,因此在每次迭代时,都会创建一个新的 myvar 实例。

      【讨论】:

      • 呃,这实际上并没有执行任何操作。没有调用内部函数。也许您打算创建一个IIFE (immediately invoked function expression),但您在这里所拥有的并不会执行任何操作。
      • @jfriend00 我想表示在 for 内的函数范围内创建变量的行为,因此它的范围不会泄漏到循环之外。函数的调用方式无关紧要,也不包括在内(尽管命名函数会泄漏)。
      • 我建议你改进你的答案。它与做任何有用的事情绝对相关,因为未调用的匿名函数不做任何事情。事实上,在它被调用之前,它甚至不会创建一个新变量。
      【解决方案3】:

      通过在变量名之前不断声明var,您可以指示 JavaScript 引擎或解释器将变量重新初始化为未定义的值(undefined 而不是数字、字符串/文本、布尔值,或null) 之前分配,这将是额外的指令,减慢循环执行的速度。您还夸大了代码大小并降低了代码解析/解释/编译的速度。

      对于几乎任何应用程序,都没有功能差异,但仍然存在差异,并且在数十万或数十亿次循环执行后差异可能会很明显。但是,函数中重复的同名 VAR 声明会导致 Chrome / V8 中出现致命异常。

      更新:在变量名前使用var可证明比省略 var 慢,正如以下在 Chrome/v8 上使用 JavaScript 控制台运行的基准测试所证明的那样。

      var startTime = new Date().getTime();
      var myvar;
      for (var i=0; i<100000; i++){
          myvar = i;
      }
      console.log(new Date().getTime() - startTime);
      
      var startTimx = new Date().getTime();
      var myvax;
      for (var j=0; j<100000; j++){
      var myvax = j;
      }
      console.log(new Date().getTime() - startTimx);
      161
      169
      

      第一个测试用了 161 毫秒,第二个测试(var)用了 169 毫秒。这是 7 毫秒的差异,在多次运行基准测试后保持一致。

      整个基准测试被粘贴到 Chrome JavaScript 控制台中,然后在执行前编译,这就是为什么第一个输出不会出现在第一次调用 console.log() 的下方。

      自己试试吧!

      【讨论】:

      • 为什么这个答案被否决了?在循环中一遍又一遍地声明一个变量是不是很明显是多余的?
      • 我怀疑任何当前的 JavaScript 实现都会在每次循环中将变量重置为未定义。从技术上讲,该变量仅在函数开头声明一次。
      • @Matthew Crumley,好吧,var x = 10; var x; 证实了你的观点。这是相关部分:code.google.com/p/v8/source/browse/trunk/src/ast.cc#90?
      • Daniel 我已经从@katspaugh 评论中完成了下面的测试,并且 var x 没有再次初始化,它仍然有 10 个值。
      • 我已经阅读了之前没有想到的规范,它说:“如果 varAlreadyDeclared 是假的,那么”(Ecma-262, 10.5),基本上,声明它并设置为undefinedno else-clause.
      【解决方案4】:

      JSLint 等工具建议您将所有 var 语句放在函数的顶部。这是因为如果你不这样做,JavaScript 本质上会为你做这件事,所以如果你这样做,它就不会那么混乱。在您的示例中,将var 放在哪里并不重要,只要它出现在myvar 的一个定义之前。同样,您也可以在函数顶部声明i

      更有趣的是分层的作用域链,当 JavaScript 想要查找名称时,它会在其中搜索名称。它从 localglobal 向上搜索范围链,直到找到所述名称的第一个实例。

      这就是为什么你可以玩这样的游戏来惹恼你的朋友:

      function foofinder() {
          var bar = function () { return foo; },
              foo="beers";
          return bar();
      }
      
      foofinder();
      >>> "beers"
      

      【讨论】:

      • 没有“var 语句”。有一个变量声明,它以关键字var 开头。 it doesn't matter where you put var as long as it occurs before one definition of myvar 这句话对我来说没有意义。声明会在任何代码执行之前进行处理,因此您可以在对其进行赋值的下方声明变量(尽管这样做可能会令人困惑)并且它们的范围仍将由声明控制。
      • 一些古老的资源将var的使用称为“var声明”。一个这样的资源是JSLint(在页面上搜索“var 语句”。)虽然var 关键字确实用于变量声明,但您当然可以在同一个表达式中进行变量定义以方便起见。如果在同一范围内定义和重新定义变量,则可以将 var 关键字放在这些定义中的任何一个之前。
      • 在检查时,确实有一个 VariableStatement (ECMA-262 § 12.2),所以我的立场是正确的。您的措辞不清楚(对我来说),我的纠正尝试使情况变得更糟。 :-(
      【解决方案5】:

      不,没有区别;在 JavaScript 中,变量的作用域是函数级别,而不是块级别。

      【讨论】:

      • 虽然这个答案似乎过于简单。这是绝对正确的。老实说,重点就是这么简单。如果你想高效地使用 Javascript,你必须了解函数作用域和块作用域,+1
      • 循环内的变量范围在连续迭代时不会改变。这个答案和你对它的支持是不正确的。有区别,尽管它很微妙,不应该改变任何脚本的执行。我已经在下面详细说明了。
      猜你喜欢
      • 2019-03-25
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-05
      • 1970-01-01
      相关资源
      最近更新 更多