JavaScript 學習之路-原型和原型鏈
面嚮物件語言(Object-Oriented) 都有類的概念,通常用 class
關鍵字來定義。在最新的 ES6 標準裡,也加入了類的使用,這意味著,js 也能像其它 OO 語言那樣很方便地實現繼承了。但是,js 的 class
其實只是個語法糖,它依然是通過 原型鏈 來實現繼承的。
在理解原型鏈之前,首先要理解一句話, 建構函式、原型和例項三者的關係:每個建構函式都有一個原型物件,原型物件都包含一個指向建構函式的指標,而例項都包含一個指向原型物件的內部指標。
理解 prototype、proto和 constructor
上面這句話對應著幾個屬性: prototype
, __proto__
和 constructor
。
每個函式在被建立的時候,都會獲得一個 prototype
屬性,這個 prototype
屬性會包含一個指標,叫做 constrcuctor
,這個 constructor
的值是建構函式的名稱。所以對應了上面那句話: 每個建構函式都有一個原型物件,原型物件都包含一個指向建構函式的指標 。我們通過 Chrome 控制檯來驗證一下:

Chrome
可以看到,我們定義了一個函式,函式名為 f ,f 的原型 prototype 裡的 constructor 最終是等於它的建構函式名稱的,那麼這個 constructor
屬性究竟是用來幹嘛的?在查閱了網上相關資料後,我發現它是 JavaScript 語言設計的歷史遺留物,它的值是可以手動變更的,但我們習慣於讓它的值指向建構函式。
看上面的圖仔細看會發現函式的 prototype 裡還有一個 __proto__
屬性。這個屬性是每個物件都會有的隱式屬性。那在 JS 這門語言裡,萬物皆物件。你在 console 裡輸入 typeof f
會輸出 function
,但 function
也是物件。不信你在輸入 Function instanceof Object
看看結果。這個 __proto__
對於實現繼承有非常重要的作用, 它始終指向物件的建構函式的原型。 這個屬性對於我們理清兩個物件之間的關係有很大的作用。
當我們 new 一個物件的時候,實際上就是生成了該物件的例項,這個例項物件會獲得一個 __proto__
屬性,連結到它的建構函式原型。這樣下來就好理解了,前面我們定義的 function f(){}
實際上 function
只是一個語法糖,為了好看而已,它的內部是做了一個 new Function()
的實現, Function
可以理解為所有函式的老大,我們平常使用關鍵字定義的函式內部都是去 new Function 實現的。
那既然函式 f 是 Function 的例項,那它肯定會獲得一個 __proto__
屬性,這個屬性是指向它的建構函式 Function 的原型 prototype 的。眼見為實,我們來驗證下:

image
對於開頭提到的建構函式、原型和例項三者的關係,我想你應該有了概念了。為加深理解,我把它畫成了圖,如下:

image
原型鏈
在理解了原型之後,我們來看下原型鏈。讓一個原型去繼承另一個原型的屬性和方法,就組成了原型鏈。就像前面的 finstance 是函式 f 的例項,f 又是 Function 的例項,... 就這樣一層一層下來,就組成了原型鏈。我們來看一段程式碼:
function Dad(){ this.name = 'xiaoming'; } Dad.prototype.getName = function(){ return this.name; } function Son(){ this.age = 18; } Son.prototype = new Dad(); var baby = new Son(); console.log(baby.getName());// xiaoming
上面這個就是繼承的一種最簡單的實現方式了,我們來看下它的原型鏈,首先 Dad 是父類,它有自己的屬性 name,它的原型裡定義了一個 getName 的方法。接著有一個子類 Son 繼承 Dad ,它也有自己的屬性 age,我們通過設定 Son.prototype = new Dad();
來實現原型繼承,這樣子類 Son 可以訪問到父類 Dad 的屬性和方法,所以子類的例項 baby 可以獲取到 name 屬性和 getName() 方法。
我們通過一張圖來看下它的原型鏈:

image
總結
__proto__
屬性通過將物件和原型之間聯絡起來來組成原型鏈;理解原型鏈,先要理解 prototype 、constructor 和 proto 這幾個概念,理清建構函式、原型和例項三者的關係,結合實際效果來驗證。JS 不像其它 OO 語言那樣,它是通過 原型鏈 來實現繼承的。這部分內容比較晦澀比較繞,耐心地多看多實踐,不懂的網上查閱資料,也許就有所理解。