为什么这个闭包作用域变量会失去其值?

6
我看到了这个JavaScript测验:http://www.netfxharmonics.com/2008/01/NetFX-Harmonics-JavaScript-Quiz,但是我无法解决其中的问题:
(function(){
    var a = 1;
    var b = 2;

    (function( ) { a = b; var b; })( );

    console.log('a:'+ a);  // => "a:undefined"
    console.log('b:'+ b);  // => "b:2"
})()

然而,如果你从内部函数中删除var b;声明,那么a == 2就像你期望的一样。

为什么会发生这种情况呢?

(你可以在这里测试:http://jsfiddle.net/gnhMZ/)

2个回答

8
这是因为这个函数:
(function( ) { a = b; var b; })( );

...将a赋值为undefinedvar的生效时间是在它所在的作用域的开始处,而不是在代码逐步执行时的位置。而当你声明一个变量时,它的初始值为undefined。因此,上述代码更加明确,但功能完全相同,看起来像这样:

(function( ) {
    var b = undefined;
    a = b;
})( );

具体来说,当执行进入一个“执行上下文”时,以下事情会发生:
  1. 一个幕后的“变量对象”将为执行上下文创建并放置在作用域链(用于解决未限定引用的变量对象链)的顶部。
  2. 对于上下文中声明的每个var,都会在该变量对象上创建属性,无论var语句在何处声明。每个变量的初始值为undefined。此时不处理初始化器。
  3. 对于在上下文中声明的每个函数(使用函数声明而不是函数表达式),都会在变量对象上创建属性,无论函数声明在何处。
  4. 处理函数声明,并将结果分配给这些函数的属性。
  5. 继续执行上下文中逐行代码的第一行。遇到带有初始化器的var语句时,将其处理为简单赋值语句。
顺便说一下,“变量对象”也是闭包工作的原理之一。基本上,当创建一个函数时,它会获得对所有在该点上作用域链中的变量对象的持久引用。这就是它用来查找它关闭的变量的。这很重要,因为闭包不仅对它实际使用的变量具有持久引用,而且对定义它的范围内的所有变量都具有持久引用,无论它们是否使用这些变量,这可能会对这些变量的生命周期产生影响。更多内容请参见此处

优秀的解释。如果我们有(function(){a=b; var b=3;})()作为内部函数,另一个能够说明清楚的案例就是那将等同于(function(){var b; a=b; b=3;})(),对吗? - Sam
@Sam:(谢谢。)完美,没错。那就是它了。好的例子,太棒了。 - T.J. Crowder
等一下..这也意味着,如果您在函数下面有一个var语句,然后返回该函数,那么闭包将包含该变量? - Sam
@Sam:是的,有两个原因:1. 因为 var 在任何逐步代码之前被处理,2. 因为闭包引用变量对象,而不是单个变量。 - T.J. Crowder

1
一个很好的解释,但简单的答案是,“a”变量没有在内部函数中声明。因此,它成为全局作用域,取代了外部作用域的值。
a =“未定义”; // 全局作用域
var = 1; // 相对于其作用域

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接