首先呢,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裏面的值,原對象也就跟着變了。編碼
很簡單吧,那麼若是執行以下代碼的話,會發生什麼呢?prototype
myObj.a = 10;
是否是認爲,還跟上面那個同樣的,obj.a也變成10了呢?實際上不是的,他的運行機制要比我們想的稍微複雜一點點。code
實際上要分三種狀況來看:對象
若是在prototype鏈上存在這個屬性,而且沒有標記爲只讀,那麼就會在本對象上新建一個新的同名屬性。原型鏈
若是在prototype鏈上存在這個屬性,而且標記爲只讀,那麼將沒法修改已有屬性或在本對象上新建一個同名屬性,若是是嚴格模式的話,還會報錯。get
若是在prototype鏈上只是存在此setter,那麼必定會調用此setter,並不會添加屬性到對象上,更不會從新定義這個setter原型
很枯燥是吧,來看例子,對照着上面的狀況,好好的理解一下:it
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
假如上面的已經理解了,那麼能夠思考下下面的運行結果:io
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爲止,這樣就組成了一個原型鏈,原型鏈也就是互相引用的引用鏈。而這個引用鏈是能夠根據本身的需求去改。
好了,簡短的一小節就完事了,若是有不明白的,或者有疏漏的地方,或者有什麼地方想和我討論的,能夠留言給我哦