夜深風竹敲秋韻,萬葉千聲皆是恨。瀏覽器
原型鏈對於JavaScript來講是個很核心的概念。JavaScript不是基於類模板的面嚮對象語言;反而,它的面向對象機制是基於原型的。咱們不可能說某個對象屬於什麼類,但卻能夠獲得某個對象的原型對象。原型對象至關於一個父級代理,當屬性在某個對象中找不到時,就會委託該對象的原型去查找。函數
JavaScript的每一個對象,均可以有一個隱式的連接(名爲__proto__
),指向它的原型對象。此次,我冒天下之大不韙(__proto__
是私有屬性,不能直接對它進行操做),顯示地定義幾個對象和它們的原型關係。this
a = {k1: 'a1'}; b = {k1: 'b1', k2: 'b2'}; c = {k1: 'c1', k2: 'c2', K3: 'c3'}; a.__proto__ = b; b.__proto__ = c; c.__proto__ = undefined;
上面的例子中,咱們首先定義了三個對象a,b,c。接下來構造它們的原型關係。其中a的原型是b,b的原型是c,c沒有原型。a、b、c造成了一條鏈式結構,這條鏈式結構在c處終止。這樣一條鏈式結構就是原型鏈。es5
原型鏈的做用主要在於對象的取值操做。當咱們根據屬性名從對象中取值時,首先會在當前對象中查找。若是在當前對象中查找不到,就會上升到該對象的原型中繼續查找。若是仍然查找不到,就會繼續上升到原型的原型……這個過程會一直持續下去,直到在某一次查找到或者原型鏈終止。因此下面的返回結果是顯然的:prototype
a.k1 //=> 'a1' a.k2 //=> 'b2' a.k3 //=> 'c3' a.k4 //=> undefined
然而對象的設值和刪值就不會參考原型鏈了,它只是對當前對象的操做。下面的例子具備必定的啓發性:代理
delete a.k2 //無效,a中沒有k2屬性 a.k2 //=> 'b2' a.k2 = 'a2' //只會影響對象a,不會影響對象b a.k2 //=> 'a2' delete a.k2 //有效,刪除a的k2屬性 a.k2 //=> 'b2' a的k2屬性被刪除,b的k2屬性暴露了出來
原型鏈的做用主要有如下兩個方面。code
若是多個對象共享一些屬性和方法,那就讓這些對象指向同一個原型,在原型中定義這些屬性和方法。這樣共享的屬性和方法只用在原型處一次定義,而無需在每一個對象中重複定義。對象
當對象a想要繼承b的屬性和方法時,只需簡單地將b定義爲a的原型便可。繼承
關於繼承,還有一點補充。除了原型繼承以外,將對象b的屬性和方法拷貝到a中去也能實現繼承。這裏b的屬性和方法就直接存在於a中,而不是經過原型獲取。ip
構造器函數能夠幫助咱們構建原型鏈。構造函數的特點以下:
//通常構造器函數首字母大寫 function Foo(name) { this.name = name; //通常不用返回任何值 }
構造器函數中,this綁定的是一個新建的對象,而且函數默認會返回這個對象。當定義構造器函數時,不要顯示地返回一個值,除非你知道本身是在作什麼。
每一個函數都有一個名爲prototype連接,它指向一個對象。當函數做爲普通函數調用時,這個連接沒什麼用處。只有當它做爲一個構造器函數調用時,它才與原型鏈構成聯繫。其實很簡單,構造器函數新建的對象,其原型就是該函數的prototype連接的對象。因此必定有下面的關係:
new Foo().__proto__ === Foo.prototype;
關於屬性共享和繼承的策略,能夠對應到構造器函數中去。因爲Foo.prototype就是new Foo()的原型,因此將共享屬性和方法放到Foo.prototype中去就能夠了。
function Foo(name) { this.name = name; //對象的示例屬性要綁定到this上 } //對象的共享屬性和方法綁定到Foo.prototype上 Foo.prototype.attr = 'attr'; Foo.prototype.foo = function() {};
繼承的實現其實有不少種方式,但很難找出直接的方式。例如咱們有構造器函數A,B,C,如今想要C繼承自B,B繼承自A,能夠經過下面的方式實現:
function A() {} function B() {} function C() {} A.prototype = new B(); B.prototype = new C();
不是很直觀,但確實作到了繼承。老實說,這不是最好的方式,由於中間對象採用new B()和new C()的方式構造,將B和C的實例變量引入到了原型鏈中來了。
爲了行文上的方便,我在上面多處提到了__proto__
。可是不要經過__proto__
隱式連接去處理原型,__proto__
不是JavaScript的標準,不一樣瀏覽器對於它們有不一樣的解釋。也就是說,咱們不能像下面這樣作:
a.__proto__ = b; //=>不能像這樣構造原型關係 a.__proto__; //=>不能像這樣獲得對象的原型
ES5(EMCAScript5)中Object對象增長了兩個新的方法,分別是create和getPrototypeOf,分別實現上面的兩種效果。上面的例子就能夠像下面這樣改寫了。
a = Object.create(b); //返回一個以b做爲原型的新對象 Object.getPrototypeOf(a); //返回a的原型對象
方法參考: