共计 2578 个字符,预计需要花费 7 分钟才能阅读完成。
首先呢,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 为止,这样就组成了一个原型链,原型链也就是互相引用的引用链。而这个引用链是可以根据自己的需求去改。
好了,简短的一小节就完事了,如果有不明白的,或者有疏漏的地方,或者有什么地方想和我讨论的,可以留言给我哦
本文转载自 http://www.lht.ren/article/8/