全球背景下的功能

时间:2013-12-19 16:14:32

标签: javascript scope closures

我知道没有“new”关键字调用的函数会将所有属性都吐出到全局上下文中。但我看到一些奇怪的行为,使用这段Javascript代码:

function Test3() {
  var a=0;

  this.inc = function() {
    return ++a;
  };

  this.noInc = function() {
    return a;
  };

  this.testRef = function() {
    return this;
  };

  return {
    inc: inc,
    testRef: testRef,
    noInc: noInc
  };
}

var o = Test3();      // Put func properties on global context
var o2 = Test3();     // Put func properties on global context (replacing properties above??)

// Both "o" and "o2" maintain their own copy of "a" (closure)

alert("o:  " + o.inc());        
alert("o:  " + o.inc());        
alert("o:  " + o.inc());        // Will output 3 (as expected)

alert(noInc());                   // Output: 1 (This seems to not be affected by o.inc() calls - expected)

// However...
alert("o2: " + o2.inc());
alert("o2: " + o2.inc());
alert("o2: " + o2.inc());       
alert("o2: " + o2.inc());       // Will output 4 (as expected)

alert(noInc());                 // Will output 4 (seems to share with o2), but why?


alert(o === window);             // false    
alert(o.testRef() === o);        // true     (I thought testRef() would be on global context?)
alert(o.testRef() === window);   // false    (^^)

alert(o2 === window);            // false
alert(o2.testRef() === o2);      // true     (I thought testRef() would be on global context?)
alert(o2.testRef() === window);  // false    (^^)

alert(testRef() === window);     // true     (How come this is here? Look at comments above)
  1. 当我们致电var o = Test()时,这里到底发生了什么? Test()执行什么上下文。由于缺少new关键字,我相信,this内的Test3()会引用窗口? “o”是指什么?它只是在全局上下文中声明的变量吗?

  2. 如果以上是真的,那么o和o2如何能够维护Test3的局部变量“a”的单独副本。我知道我们在这里已经关闭了,但是后来如何,“o2”和“window”共享变量“a”的相同副本,但不是“o”

  3. 当我执行var o = Test3()然后执行alert(o.testRef()=== window)时,它会显示false。所以在执行之后:

    var o = Test3();
    var o2 = Test3();
    
  4. Test3()似乎有3个属性副本。一个在“o”上,另一个在“o2”上,另一个在全球背景下。

    但是怎么可能有“o”和“o2” - 我没有用“new”关键字调用Test3(),所以这只应该引用全局上下文?

2 个答案:

答案 0 :(得分:1)

您的代码可以缩减为:

var inc, noinc, a = 0, a2 = 0;
inc = function () {
   a += 1;
}
noinc = function () {
   return a;
}
inc();
inc();
inc();
noinc(); // 3

inc = function () {
   a2 += 1;
}
noinc = function () {
   return a2;
}
// now inc and noinc are bound to a2
noinc(); // 0, yours gives 1?

所以基本上你要覆盖与inc绑定的noinca的定义。

如果您var a = 0代替this.a = this.a || 0,则会绑定到相同的 a

答案 1 :(得分:1)

每次拨打Test3时,incnoInctestRef功能都会重新分配给window,其中a({在函数顶部初始化为0。)

windowo2共享相同的a,因为当window获得返回值而非o2时,函数已重新分配给o1

来自NodeJS REPL的示例(注意在NodeJS中,全局对象由global而不是window引用,但是否则应该没有任何区别):

> function Test3() {
...   var a=0;
... 
...   this.inc = function() {
...     return ++a;
...   };
... 
...   this.noInc = function() {
...     return a;
...   };
... 
...   this.testRef = function() {
...     return this;
...   };
... 
...   return {
...     inc: inc,
...     testRef: testRef,
...     noInc: noInc
...   };
... }
undefined
> var o = Test3();
undefined
> o.inc()
1
> o.inc()
2
> o.inc()
3
> noInc()
3

由于Test3仅被调用一次并设置为o1,因此全局范围和o1共享a。现在看我再次致电Test3

> Test3()
{ inc: [Function],
  testRef: [Function],
  noInc: [Function] }
> noInc()
0
> o.noInc()
3

将函数重新分配给全局对象,a0,而o保留之前的a