函数对象的Prototype对象是所有实例共享的,
New一个对象a出来的时候建立了一个引用,让它的a.__proto__
指向了函数对象的prototype
但是,如果动态改变函数的prototype,假如让它变成空{}
这时再new一个对象b出来的时候,它的b.__proto__
也指向了函数对象prototype,但是却是空
而之前的a.__proto__
的属相好像没有变化?这是说明a和b的proto引用的不是同一块内存?这个跟实例共享prototype是不是矛盾了?
函数对象的Prototype对象是所有实例共享的,
New一个对象a出来的时候建立了一个引用,让它的a.__proto__
指向了函数对象的prototype
但是,如果动态改变函数的prototype,假如让它变成空{}
这时再new一个对象b出来的时候,它的b.__proto__
也指向了函数对象prototype,但是却是空
而之前的a.__proto__
的属相好像没有变化?这是说明a和b的proto引用的不是同一块内存?这个跟实例共享prototype是不是矛盾了?
哪里有矛盾? 继续new b2出来的时候 b2 和 b的原型就是同一个对象了
Javascript中 var a = new A();
执行完毕后,a和A就不再有任何的强绑定关系了,只有a.__proto__ === A.prototype
这个弱关系(而且还不一定成立!),你覆盖掉了A.prototype
之后,自然a和A就不再有任何关系
var a = new A()
大致等价于下面的代码
var a = (function() {
var context = Object.create(A.prototype);
var ret = A.call(context);
return ret !== null && typeof ret === 'object' ? ret : context;
})()
假设有这样的代码:
function Test() {}
Test.prototype = {x: 1};
var a = new Test();
Test.prototype = {y: 2};
var b = new Test();
console.log(a.x); // 1
console.log(b.y); // 2
为什么a.__proto__
没跟着变?
首先,要了解清楚,a
与b
共享的只是Constructor.prototype
的指向,而不是内存,也就是每次new一个Constructor
的时候,js都会new一个object,假设为ref,然后把ref的__proto__
指向Constructor.prototype
,这是每次创建一个实例都需要经过的步骤,如果你在new之前改变prototype
,ref.__proto__
指向的将是不同的东西!
为什么a
与b
共享的proto
引用不是同一块内存?
原因就是你把prototype
指向了一个新的对象,破坏了引用。(指向了一个空对象,这会重新分配内存,原有内存空间还在,只不过现在不能通过Test.prototype
来访问而已)
类似的还有函数形参等情况:
function test(obj) {
// case 1:
// 在函数里面重新赋值了,obj指向了新的内存空间,不再与temp有关联
obj = {x: 1};
// case 2:
// 这里不重新分配内存空间,只是为obj添加属性的话,就会同时更新外部的temp对象
// obj.y = 3;
console.log(obj);
}
var temp = {y: 2};
test(temp);
// 如果test函数中没有破坏temp引用的话,temp就不会受到影响
console.log(temp);
更经典的还有nodejs的exports
与module.exports
。
一楼答案的例子已经可以说明这个现象的原因了,我这里补充一下。
理解这个现象,首先要了解JS里面new
操作符执行的几个动作,也就是当new Foo()
执行的时候发生了什么事情,我这里简单概括一下:
- 首先创建一个空对象,这个空对象的类型是
Foo
,并且空对象的隐式属性__proto__
指向了Foo.prototype
所引用的对象;- 调用构造函数,进入上下文,执行上下文的
thisValue
,也就是this
的值指向刚才创建的对象,然后函数代码执行;- 隐式返回刚才创建的对象(如果函数里面没有显式返回值得话);
理解了这个过程之后,题主的现象就很容易解释了,当你创建实例a的时候a.__proto__
指向了Foo.prototype
引用的对象Foo {}
,当你给Foo.prototype
赋值为{}
的时候,Foo.prototype
就指向了你创建的这个空对象,原理类似于:
var foo = {x: 10},
bar = {y: 10};
var w = foo,
z = w,
w = bar;
console.log(b); // foo {x: 10}
Foo
的实例a
相当于例子中的z
(可能有点绕),例子中w
指向了另外的对象,但是z
还是指向原来的对象,就像你的Foo.prototype
指向了另外一个对象,但是a.__proto__
却还是指向原来的那个对象。
这时候当你再次执行var b = new Foo()
时,还是执行上述的过程,b.__proto__
指向了Foo.prototype
引用的对象,也就是你赋值的那个对象,明显这个对象和a.__proto__
引用的不是一个对象。
还有就是你提到的
实例共享
prototype
这句话有点断章取义了,而且现在很多技术用语不是很严谨,更不能像记口诀一样去学习知识,我们需要了解他的本质过程。
希望我的回答对你有帮助!
没矛盾的,对象是引用传递,当你给实例赋新值的时候,它引用的就会是内存中重新分配一个新地址,跟之前的那个引用已经脱离关系,所以你更改当前的任何属性和方法都不会对之前的应用有任何改变。
8 回答4.7k 阅读✓ 已解决
6 回答3.4k 阅读✓ 已解决
5 回答2.8k 阅读✓ 已解决
5 回答6.3k 阅读✓ 已解决
4 回答2.2k 阅读✓ 已解决
4 回答2.8k 阅读✓ 已解决
3 回答2.4k 阅读✓ 已解决
题主指的赋值为空,是指的这样吗?
我们此时看下
b.__proto__
,可以看到是:其实上面所谓赋空值的如同下面代码:
这下应该明白了吧,我们还可以继续举个更极端的例子:
此时,我们输出
b.__proto__
,可以看到:这是怎么搞的,我们来看下
b.constructor
:那么上面这个例子跟下面代码也是等同的:
我相信这下应该明白,你并不是将
prototype
赋值为空,而是将原型链指向了另外一个对象实例。其实你的赋值为空等同于: