【问题标题】:JS: Which / How are variables stored in closures?JS:哪些变量/如何存储在闭包中?
【发布时间】:2015-02-06 05:19:50
【问题描述】:

考虑以下代码:

    var a = [ { a: 'I' }, { a: 'II' }, { a: 'III' } ];

    for( var i=0; i<a.length; i++ ){

            var j = i;
            var x = a[ i ];

            x.bb = function(){
                    x.b = "-" + x.a + "-";
            }
            x.cc = function(){
                    a[ i ].c = "-" + a[ i ].a + "-";
            }
            x.dd = function(){
                    a[ j ].d = "-" + a[ j ].a + "-";
            }
    }

    for( var i=0; i<a.length; i++ ){
            a[i].bb();
            a[i].cc();
            a[i].dd();
    }

    console.log( a );

将导致(遗漏的功能):

[ { a: 'I', c: '-I-' },
  { a: 'II', c: '-II-' },
  { a: 'III', b: '-III-', d: '-III-', c: '-III-' } ]

所以只有 cc() 做我打算做的事情。

我现在的问题是:

  1. 这里的变量如何存储在闭包中的规则是什么。 (显然i 被存储为常量,而jx 被存储为对局部变量的引用,这些变量在 for 循环迭代时会发生变化。但这是为什么呢?) 这是我的错误。该代码之所以起作用,是因为第二个和第一个 i 相同!
  2. 有没有办法编写bb() 以存储对a[ i ] 的引用,这样我就不必使用索引了?

谢谢!

【问题讨论】:

  • 这里没有闭包。
  • 抱歉措辞错误。那么,您将如何命名这些存储对周围范围的引用的函数?
  • 闭包通过保持对外部变量环境的引用来工作。在这种情况下,您的所有函数都具有相同的外部变量环境(即全局范围)。因此,当您在函数内部引用ij 时,您指的是存在于函数外部的全局ij。如果您想捕获一个,而不是对(可能变化的)变量的引用,请参阅JavaScript closure inside loops – simple practical example

标签: javascript node.js closures


【解决方案1】:

您需要记住,JavaScript 没有块作用域(好吧,反正还没有,ES6 is working that in with let),相反,一切都在“函数”级别。也就是说,所有变量的作用域都是它们最近的函数定义。

变量是使用var 语句声明的,并且根据您的代码,您拥有的每个变量的作用域都是此代码的外部容器。 换句话说,如果此代码位于Node 模块,那么这里的所有变量都作用于整个模块。因此,尽管您在for 循环中定义的函数是闭包(因为所有 JS 函数都是闭包),但它们仍然只是使用整个模块中定义的变量,因为 JS 不需要进一步查看。

为了说明问题 1 的答案,请考虑以下几点:

var o = {};
var x = 1;

function foo() {
    var x = 2;
    o.bar = function() {
        console.log(x);
    }
}

o.bar(); // will output "2" because of the closure created in `foo()`

至于你的第二个问题,因为 a[i] 是一个对象,所以你将该对象分配给的任何变量(在本例中为x)都将是一个引用,并且所有引用都将指向内存中的同一个对象.我不是您要使用bb() 完成的任务,但您正在使用x“存储对a[i] 的引用”。也许你有更具体的例子?

【讨论】:

  • 啊。真正让我感到困惑的是它似乎与i 一起工作。但是现在我注意到我的第一个 i 和我的第二个是相同的(我希望一些理智的解释器会抱怨第二个 var)并且在第二个循环中使用另一个变量会导致失败。
  • @Scheintod 仅供参考,重复的 var 语句在 ES5 规范 section 10.5, step 8 中被明确允许(或被允许忽略)。
  • @apsillers 提出了一个很好的观点,这就是为什么您应该在 JavaScript 中始终使用 strict mode,这将迫使解释器将重复的 var 语句作为错误(应该如此)!很简单,只需将"use strict;" 放在函数/模块的顶部即可。
猜你喜欢
  • 2010-12-17
  • 2015-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-29
相关资源
最近更新 更多