首先呢,prototype是对象里的一个内置属性,并且呢,这个属性是对于其他对象的一个引用。所以呢,思考下面的例子:
var obj = {
a: 2
}
var myObj = Object.create(obj);
console.log(myObj.a); // 2
console.log(myObj === obj); // false
console.log(Object.getPrototypeOf(myObj) === obj); // true
Object.getPrototypeOf(myObj).a = 4
console.log(obj.a); // 4
这里可以看到,实际上Object.create()是新建了一个对象,并且这个对象的prototype是obj的一个引用,所以呢,如果咱们直接修改prototype里面的值,原对象也就跟着变了。
很简单吧,那么如果执行如下代码的话,会发生什么呢?
myObj.a = 10;
是不是认为,还跟上面那个一样的,obj.a也变成10了呢?实际上不是的,他的运行机制要比咱们想的稍微复杂一点点。
实际上要分三种情况来看:
如果在prototype链上存在这个属性,并且没有标记为只读,那么就会在本对象上新建一个新的同名属性。
如果在prototype链上存在这个属性,并且标记为只读,那么将无法修改已有属性或在本对象上新建一个同名属性,如果是严格模式的话,还会报错。
如果在prototype链上只是存在此setter,那么一定会调用此setter,并不会添加属性到对象上,更不会重新定义这个setter
很枯燥是吧,来看例子,对照着上面的情况,好好的理解一下:
var obj = {
a: 2,
set c(num) {
console.log('exec it');
}
}
var myObj = Object.create(obj);
myObj.a = 10;
console.log(obj.a); // 2
console.log(myObj.a); // 10
Object.defineProperty(obj, 'b', {
value: 3,
writable: false
})
myObj.b = 10;
console.log(myObj.b); // 3
myObj.c = 20; // "exec it"
console.log(myObj.c); // undefined
假如上面的已经理解了,那么可以思考下下面的运行结果:
var obj = {
a: 2
}
var myObj = Object.create(obj);
console.log(++myObj.a); // 3
console.log(obj.a); // 2
这个在咱们实际的编码中时有发生,看代码是想把a改成3,但是由于上面第一种情况的影响,实际上是新建了一个同名属性3,并且赋值给了myObj。
上面我们谈论的都是普通对象的prototype的一些特性,接下来,咱们就要讲关于new关键字相关的一些知识点了,思考下面的例子
function Foo() {}
var a = new Foo();
console.log(Object.getPrototypeOf(a) === Foo.prototype); // true
var b = new Foo();
Object.getPrototypeOf(b).saySomething = function () {
console.log('say something');
}
a.saySomething(); // "say something"
很明显,在new的过程中呢,生成了一个新对象,并且把Foo.prototype引用到了新对象的prototype。那么因为是引用,所以通过b改变其原型上的prototype的值,Foo.prototype里也会跟着改变。
那么new的过程,是不是一定引用的是函数的prototype呢?也不一定,比如说下面的例子。
function Foo() {
return {
a: 3
}
}
var a = new Foo();
console.log(Object.getPrototypeOf(a) === Foo.prototype); // false
console.log(Object.getPrototypeOf(a) === Object.prototype); // true
console.log(a.a); // 3
在这个例子中,由于new的时候,返回的是一个对象,所以最后实际上a最终引用的是Foo最后返回的那个小对象,所以其prototype就是Object.prototype,而不是Foo.prototype
甚至说,Foo.prototype也是可以被改变的,不过在这时候,new出来的对象,其prototype就是被改过的那个对象。
var protoObj = {
b: 10
}
function Foo() {}
Foo.prototype = protoObj;
var a = new Foo();
console.log(Object.getPrototypeOf(a) === Foo.prototype); // true
console.log(Object.getPrototypeOf(a) === protoObj); // true
console.log(a.b); // 10
你看,如果prototypeObj修改了默认的Foo.prototype,所以最后,实际上形成了这么一个引用链:a.prototype => foo.prototype => protoObj=>Object.prototype。
所以说结论吧,在new的时候,实际上执行会包含这么几步,
如果有return并且返回的是一个对象的话,则直接返回return后的那个对象。
反之,则新建一个对象。
并且吧函数的prototype引用到新建对象的prototype中。
所以说,原型,可以理解为我本来对象有一个prototype,引用着其他的对象,当我这个对象的prototype引用了另一个对象的prototype,一般情况会到Object.prototype为止,这样就组成了一个原型链,原型链也就是互相引用的引用链。而这个引用链是可以根据自己的需求去改。
好了,简短的一小节就完事了,如果有不明白的,或者有疏漏的地方,或者有什么地方想和我讨论的,可以留言给我哦。