首先呢,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/