Functions in global context

2019-07-20 04:49发布

I understand that functions called without the "new" keyword spit out all their properties on to the global context. But I am seeing some curious behavior, with this piece of Javascript code:

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. When we call var o = Test(), what exactly happens here? What context does Test() execute in. Since the new keyword is missing, I believe, this inside Test3() would refer to window? What does "o" refer to? Is it simply a variable declared on the global context?

  2. If above is true, then how are o and o2 able to maintain separate copies of Test3's local variable "a". I understand that we have closure in action here, but then how come, "o2" and "window" share the same copy of the variable "a", but NOT "o"

  3. When I do var o = Test3() and then do alert(o.testRef() === window), it says false. So after executing:

    var o = Test3();
    var o2 = Test3();
    

There seems to be 3 copies of the properties from Test3(). One on "o", another one on "o2" and one on the global context.

But how can there be any on "o" and "o2" - I am not calling Test3() with the "new" keyword, so this should only refer to global context?

2条回答
我命由我不由天
2楼-- · 2019-07-20 04:57

Every time you call Test3, the inc, noInc and testRef functions are reassigned to window with a different a (which is initialized to 0 at the top of the function.)

window and o2 share the same a because the functions were reassigned to window when o2 got the return value and not o1.

Example from NodeJS REPL (note in NodeJS, the global object is referenced by global instead of window, but otherwise there shouldn't be any difference):

> 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

Because Test3 was only called once and set to o1, both the global scope and o1 share an a. Now watch as I call Test3 again:

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

The functions are reassigned to the global object, with an a of 0, while o keeps the previous a.

查看更多
孤傲高冷的网名
3楼-- · 2019-07-20 05:18

Your code can be reduced to:

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?

So basically you're overriding the definition of inc and noinc bound to a different a.

If instead of var a = 0 you had this.a = this.a || 0 you would bind to the same a.

查看更多
登录 后发表回答