考虑以下代码,
function Test(){
var a = 'prabhu';
Test.prototype.print = function (){ alert(a); a=10; }
}
我正在为Test创建对象实例,
var x = new Test();
var y = new Test();
现在访问原型中可用的两者的print
方法
x.print() //"prabhu" ok!!
x.print() //10 Double ok!!
但是
y.print() //10 what the?? It should be prabhu. Isn't it?
这是怎么回事?因为在创建y
实例时,将调用constructor
并且它将覆盖其原型中的print
函数。它会为值closure
的{{1}}创建a
。因此,通过"prabhu"
访问它时,它应该打印print()
吗?为什么这不会发生?对这种行为的逐步解释将非常有用。
P.S:如果我们在任何地方使用"prabhu"
代替this.a
和a
,我就知道代码的行为。所以请不要建议使用它。我努力弄清楚为什么上面解释的行为正在发生。
答案 0 :(得分: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();
此时,我们已将此记录在内存中(省略了一些不相关的细节):
+---------------+ x-->| (object) | +---------------+ +----------+ | [[Prototype]] |---->| (object) | +---------------+ +----------+ +-----------------+ | print |-->| (function) | +----------+ +-----------------+ +---------------+ | env |-->| Environment 1 | | alert(a); a=10; | +---------------+ +-----------------+ | a: 'prabhu' | +---------------+
现在我们这样做:
var y = new Test();
现在我们有:
+-----------------+ +---------------+ +---------------+ | (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
创建的 new 环境。
旧功能和它用来关闭的环境现在都有资格进行垃圾收集。
然后:
x.print() //"prabhu" ok!!
给我们(我现在已经离开了垃圾;也许它被收集了,也许不是,没关系):
+-----------------+ +---------------+ +---------------+ | (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!!
不会改变任何东西。
最后:
y.print() //10 what the?? It should be prabhu. Isn't it?
出于完全相同的原因显示10 x.print()
显示10:print
函数正在使用第二次调用a
中的Test
,而不是第一个。