【问题标题】:Functions in global context全局上下文中的函数
【发布时间】:2014-01-08 07:17:15
【问题描述】:

我知道调用没有“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 关键字,我相信Test3() 中的this 会引用窗口? “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();
    

Test3() 的属性似乎有 3 个副本。一个在“o”上,另一个在“o2”上,一个在全局上下文上。

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

【问题讨论】:

  • 帮自己一个忙,将"use strict";这一行添加到脚本的顶部。
  • 在 JSBin 中工作。我认为它默认启用了“严格”​​。尝试严格使用;但 JSBin 抱怨那条线。 “严格”会做什么?
  • @TahaAhmad 你放的是"use strict"; 还是use strict;?您应该使用带引号的那个...
  • 在“严格”模式下,在没有上下文的情况下调用的函数中this 的值是undefined 而不是window,所以这些分配都会失败。
  • 我明白了......但是,在这种情况下,我想了解这里发生了什么,其中“this”隐式默认为全局上下文。

标签: javascript scope closures


【解决方案1】:

每次调用 Test3 时,incnoInctestRef 函数都会重新分配给 window,并使用不同的 a(在函数顶部初始化为 0 .)

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

来自 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

【讨论】:

  • 解释得很漂亮!
  • 一个后续问题:在我的代码中,在 var o = Test3() 之前,如果我执行 var o4 = new Test3(),则代码停止工作并给出错误 - “ReferenceError: inc is没有定义的”。这是怎么回事?
  • @TahaAhmad:由于this 不再引用windowthis.inc = function() {...} 创建全局变量,return { inc: inc, ...} 抛出错误,因为@ 987654351@ 未在任何范围内定义。
  • 艾芬太棒了!谢谢!
【解决方案2】:

您的代码可以简化为:

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?

所以基本上你会覆盖绑定到不同aincnoinc 的定义。

如果您使用this.a = this.a || 0 而不是var a = 0,您将绑定到相同 a

【讨论】:

  • 这不是我的代码正在做的事情:o.inc()、o2.inc() 维护自己的“a”副本。然而 o2 似乎在全球范围内共享“a”
  • 这不准确。 o2 不共享 a(它是 private),只是 this.inc === o2.inc。如果你做了o3 = Test3() 现在o3 将是之前的o2,而o2 将像o
  • 优秀。但是,window.inc() 是从哪里来的,或者说 o.inc() 是从哪里来的呢? Test3() 不是在全局上下文(窗口)中执行,因此 inc() 应该只在窗口中可用。如果是,是什么导致“o”获得 inc()(以及其他属性)?
  • @TahaAhmad 在非严格的裸函数调用中(即,不是方法或事件侦听器或call/bind, etc.), this` 设置为window,所以window.inc在设置this.inc 时设置。由于window 是全局对象,inc 指的是window.inc,因为在全局inc 之前的作用域链中没有inc 标识符。跨度>
  • 是的,我明白这一点。我不明白的是 o 和 o2 是从哪里得到 inc() 的?
猜你喜欢
  • 2023-03-31
  • 2016-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多