构造函数中闭包的神秘行为

Raj*_*amy 2 javascript

考虑下面的代码,

function Test(){
  var a = 'prabhu';
  Test.prototype.print = function (){ alert(a); a=10; }
}
Run Code Online (Sandbox Code Playgroud)

我正在为Test创建对象实例,

var x = new Test();
var y = new Test();
Run Code Online (Sandbox Code Playgroud)

现在访问print他们的原型中可用的两者的方法,

x.print() //"prabhu"  ok!!
x.print() //10  Double ok!!
Run Code Online (Sandbox Code Playgroud)

y.print() //10  what the?? It should be prabhu. Isn't it?
Run Code Online (Sandbox Code Playgroud)

这是怎么回事?因为在创建y实例时,constructor将调用它,它将覆盖print其原型中的函数.它会创建一个closure用于a与价值"prabhu".因此,通过print()它访问它应该打印"prabhu"正确吗?为什么这不会发生?对这种行为的逐步解释将非常有帮助.

PS:我知道代码的行为,如果我们this.a在任何地方使用而不是avar a.所以请不要建议使用它.我努力弄清楚为什么上面解释的行为正在发生.

T.J*_*der 9

决不功能分配给prototype从一个构造函数对象的构造,它设置了这个串扰.

原因是通过创建的对象new Test具有到其原型的实时链接,而不是它的副本.这意味着如果您修改原型对象,下次对象在其上查找某些内容(例如print)时,它会看到新对象.这是一个Good Thing™,但它确实意味着你不应该Test.prototype从内部分配Test.

你正在看到你正在看到的东西,因为当你这样做时var y = new Test(),你正在改变现有的原型,修改x它的print功能,以便它a从第二个调用(创建的那个y)开始关闭,而不是第一个调用.因此,如果您x.print()在执行此操作后调用,则a更新的是第二个,而不是第一个.

让我们按照您的代码:

function Test(){
  var a = 'prabhu';
  Test.prototype.print = function (){ alert(a); a=10; }
}

var x = new Test();
Run Code Online (Sandbox Code Playgroud)

在这一点上,我们在内存中有这个(留下一些不相关的细节):

    +---------------+   
x-->|   (object)    |   
    +---------------+     +----------+     
    | [[Prototype]] |---->| (object) |     
    +---------------+     +----------+   +-----------------+   
                          | print    |-->| (function)      |   
                          +----------+   +-----------------+   +---------------+
                                         | env             |-->| Environment 1 |
                                         | alert(a); a=10; |   +---------------+
                                         +-----------------+   | a: 'prabhu'   |                
                                                               +---------------+

现在我们这样做:

var y = new Test();
Run Code Online (Sandbox Code Playgroud)

现在我们有:

                                         +-----------------+   +---------------+
    +---------------+                    | (function)      |   | Environment 1 |
x-->|   (object)    |                    +-----------------+   +---------------+
    +---------------+                    | env             |-->| a: 'prabhu'   |
    | [[Prototype]] |--+                 | alert(a); a=10; |   +---------------+
    +---------------+  |                 +-----------------+                    
                       |
                       |  +----------+   +-----------------+   +---------------+
                       +->| (object) |   | (function)      |   | Environment 2 |
                       |  +----------+   +-----------------+   +---------------+
                       |  | print    |-->| env             |-->| a: 'prabhu'   |
                       |  +----------+   | alert(a); a=10; |   +---------------+
                       |                 +-----------------+
    +---------------+  |
y-->|   (object)    |  |
    +---------------+  |
    | [[Prototype]] |--+
    +---------------+

注意旧print函数不再x以任何方式连接到对象; 它已被新的替换,它指的是通过第二次调用创建的环境Test.旧功能和它用来关闭的环境现在都符合垃圾收集的条件.

然后:

x.print() //"prabhu"  ok!!
Run Code Online (Sandbox Code Playgroud)

给我们(我现在已经离开了垃圾;也许是收集的,也许不是,无所谓):

                                         +-----------------+   +---------------+
    +---------------+                    | (function)      |   | Environment 1 |
x-->|   (object)    |                    +-----------------+   +---------------+
    +---------------+                    | env             |-->| a: 'prabhu'   |
    | [[Prototype]] |--+                 | alert(a); a=10; |   +---------------+
    +---------------+  |                 +-----------------+                    
                       |
                       |  +----------+   +-----------------+   +---------------+
                       +->| (object) |   | (function)      |   | Environment 2 |
                       |  +----------+   +-----------------+   +---------------+
                       |  | print    |-->| env             |-->| a: 10         |
                       |  +----------+   | alert(a); a=10; |   +---------------+
                       |                 +-----------------+
    +---------------+  |
y-->|   (object)    |  |
    +---------------+  |
    | [[Prototype]] |--+
    +---------------+

请注意,调用a第二次调用更改为Test(创建的y),而不是第一次调用(垃圾).

然后:

x.print() //10  Double ok!!
Run Code Online (Sandbox Code Playgroud)

什么都不改变.

最后:

y.print() //10  what the?? It should be prabhu. Isn't it?
Run Code Online (Sandbox Code Playgroud)

示出了10完全相同的原因x.print()示出了10:该print功能是使用a第二呼叫Test,而不是第一个.

  • @TJCrowder哇!!! 你的图解释了我的期望.当适用时,肯定会给你一个赏金.感谢您投入宝贵的时间.:) (2认同)