Javascript基礎之-原型(prototype)

首先呢,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爲止,這樣就組成了一個原型鏈,原型鏈也就是互相引用的引用鏈。而這個引用鏈是能夠根據本身的需求去改。

好了,簡短的一小節就完事了,若是有不明白的,或者有疏漏的地方,或者有什麼地方想和我討論的,能夠留言給我哦

本文轉載自http://www.lht.ren/article/8/

相關文章
相關標籤/搜索