Javascript 是唯一一個被廣泛運用的原型式繼承的語言,所以理解兩種繼承方式的差異是需要時間的。
第一個主要差異就是Javascript 使用原型鏈來繼承:
function Foo() { this.value = 42;}Foo.prototype = { method: function() {}};function Bar() {}設置Bar 的prototype 為Foo 的對象實例:
Bar.prototype = new Foo();Bar.prototype.foo = 'Hello World';
確保Bar 的構造函數為本身,並新建一個Bar 對象實例:
Bar.prototype.constructor = Bar;var test = new Bar();
下面我們來看下整個原型鏈的組成:
test [instance of Bar] Bar.prototype [instance of Foo] { foo: 'Hello World' } Foo.prototype { method: ... } Object.prototype { toString: ... /* etc. */ }在上面的例子中,對象test 將會同時繼承Bar.prototype 和Foo.prototype。因此它可以訪問定義在Foo 中的函數method。當然,它也可以訪問屬性value。需要提到的是,當new Bar() 時並不會創建一個新的Foo 實例,而是重用它原型對象自帶的Foo 實例。同樣,所有的Bar 實例都共享同一個value 屬性。我們舉例說明:
test1 = new Bar(); test2 = new Bar(); Bar.prototype.value = 41; test1.value //41 test2.value//41
原型鏈查找機制
當訪問一個對象的屬性時,Javascript 會從對象本身開始往上遍歷整個原型鏈,直到找到對應屬性為止。如果此時到達了原型鏈的頂部,也就是上例中的Object.prototype,仍然未發現需要查找的屬性,那麼Javascript 就會返回undefined 值。
原型對象的屬性
儘管原型對象的屬性被Javascript 用來構建原型鏈,我們仍然可以值賦給它。但是原始值複製給prototype 是無效的,如:
function Foo() {}Foo.prototype = 1; // no effect這裡講個本篇的題外話,介紹下什麼是原始值:
在Javascript 中,變量可以存放兩種類型的值,分別是原始值和引用值。
1.原始值(primitive value):
原始值是固定而簡單的值,是存放在棧stack 中的簡單數據段,也就是說,它們的值直接存儲在變量訪問的位置。
原始類型有以下五種型: Undefined, Null, Boolean, Number, String。
2.引用值(reference value):
引用值則是比較大的對象,存放在堆heap 中的對象,也就是說,存儲在變量處的值是一個指針pointer,指向存儲對象的內存處。所有引用類型都集成自Object。
原型鏈性能問題
如果需要查找的屬性位於原型鏈的上端,那麼查找過程對於性能而言無疑會帶來負面影響。當在性能要求必要嚴格的場景中這將是需要重點考慮得因素。此外,試圖查找一個不存在屬性時將會遍歷整個原型鏈。
同樣,當遍歷一個對象的屬性時,所有在原型鏈上的屬性都將被訪問。
總結
理解原型式繼承是寫較為複雜的Javascript 代碼的前提,同時要注意代碼中原型鏈的高度。當面臨性能瓶頸時要學會將原型鏈拆分開來。此外,要將原型對象prototype 和原型__proto__ 區分開來,這裡主要討論原型對象prototype 就不闡述關於原型__proto__ 的問題了,