天天看點

細說JavaScript對象(2):原型對象

JavaScript 并沒有類繼承模型,而是使用原型對象 prototype 進行原型式繼承。

盡管人們經常将此看做是 JavaScript 的一個缺點,然而事實上,原型式繼承比傳統的類繼承模型要更加強大。舉個例子,在原型式繼承頂端建構一個類模型很簡單,然而反過來則是個困難得多的任務。

JavaScript 是唯一一個被廣泛運用的原型式繼承的語言,是以了解兩種繼承方式的差異是需要時間的。

第一個主要差異就是 JavaScript 使用原型鍊來繼承:

設定 Bar 的 prototype 為 Foo 的對象執行個體:

確定 Bar 的構造函數為本身,并建立一個 Bar 對象執行個體:

下面我們來看下整個原型鍊的組成:

在上面的例子中,對象 test 将會同時繼承 Bar.prototype 和 Foo.prototype。是以它可以通路定義在 Foo 中的函數 method。當然,它也可以通路屬性 value。需要提到的是,當 new Bar() 時并不會建立一個新的 Foo 執行個體,而是重用它原型對象自帶的 Foo 執行個體。同樣,所有的 Bar 執行個體都共享同一個 value 屬性。我們舉例說明:

原型鍊查找機制

當通路一個對象的屬性時,JavaScript 會從對象本身開始往上周遊整個原型鍊,直到找到對應屬性為止。如果此時到達了原型鍊的頂部,也就是上例中的 Object.prototype,仍然未發現需要查找的屬性,那麼 JavaScript 就會傳回 undefined 值。

原型對象的屬性

盡管原型對象的屬性被 JavaScript 用來建構原型鍊,我們仍然可以值賦給它。但是原始值複制給 prototype 是無效的,如:

這裡講個本篇的題外話,介紹下什麼是原始值:

在 JavaScript 中,變量可以存放兩種類型的值,分别是原始值和引用值。

1、原始值(primitive value):

原始值是固定而簡單的值,是存放在棧 stack 中的簡單資料段,也就是說,它們的值直接存儲在變量通路的位置。

原始類型有以下五種型: Undefined, Null, Boolean, Number, String。

2、引用值(reference value):

引用值則是比較大的對象,存放在堆 heap 中的對象,也就是說,存儲在變量處的值是一個指針 pointer,指向存儲對象的記憶體處。所有引用類型都內建自 Object。

原型鍊性能問題

如果需要查找的屬性位于原型鍊的上端,那麼查找過程對于性能而言無疑會帶來負面影響。當在性能要求必要嚴格的場景中這将是需要重點考慮得因素。此外,試圖查找一個不存在屬性時将會周遊整個原型鍊。

同樣,當周遊一個對象的屬性時,所有在原型鍊上的屬性都将被通路。

總結

了解原型式繼承是寫較為複雜的 JavaScript 代碼的前提,同時要注意代碼中原型鍊的高度。當面臨性能瓶頸時要學會将原型鍊拆分開來。此外,要将原型對象 prototype 和原型 __proto__ 區分開來,這裡主要讨論原型對象 prototype 就不闡述關于原型 __proto__ 的問題了,如果有疑惑的話,可以閱讀 @nightire 凡哥的博文​​《了解 JavaScript(四)》​​。

繼續閱讀