Javascript
並無類繼承模型,而是使用原型對象 prototype
進行原型式繼承。
儘管人們常常將此看作是 Javascript
的一個缺點,然而事實上,原型式繼承比傳統的類繼承模型要更增強大。舉個例子,在原型式繼承頂端構建一個類模型很簡單,然而反過來則是個困可貴多的任務。Javascript
是惟一一個被普遍運用的原型式繼承的語言,因此理解兩種繼承方式的差別是須要時間的。git
第一個主要差別就是 Javascript
使用原型鏈來繼承:github
function Foo() { this.value = 42; } Foo.prototype = { method: function() {} }; function Bar() {}
設置 Bar
的 prototype
爲 Foo
的對象實例:segmentfault
Bar.prototype = new Foo(); Bar.prototype.foo = 'Hello World';
確保 Bar
的構造函數爲自己,並新建一個 Bar
對象實例:ide
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
值。this
儘管原型對象的屬性被 Javascript
用來構建原型鏈,咱們仍然能夠值賦給它。可是原始值複製給 prototype
是無效的,如:prototype
function Foo() {} Foo.prototype = 1; // no effect
這裏講個本篇的題外話,介紹下什麼是原始值:
在 Javascript
中,變量能夠存放兩種類型的值,分別是原始值和引用值。指針
1.原始值
(primitive value)
:
原始值是固定而簡單的值,是存放在棧stack
中的簡單數據段,也就是說,它們的值直接存儲在變量訪問的位置。
原始類型有如下五種型:Undefined, Null, Boolean, Number, String
。code
2.引用值
(reference value)
:
引用值則是比較大的對象,存放在堆heap
中的對象,也就是說,存儲在變量處的值是一個指針pointer
,指向存儲對象的內存處。全部引用類型都集成自Object
。
若是須要查找的屬性位於原型鏈的上端,那麼查找過程對於性能而言無疑會帶來負面影響。當在性能要求必要嚴格的場景中這將是須要重點考慮得因素。此外,試圖查找一個不存在屬性時將會遍歷整個原型鏈。
一樣,當遍歷一個對象的屬性時,全部在原型鏈上的屬性都將被訪問。
理解原型式繼承是寫較爲複雜的 Javascript
代碼的前提,同時要注意代碼中原型鏈的高度。當面臨性能瓶頸時要學會將原型鏈拆分開來。此外,要將原型對象 prototype
和原型 __proto__
區分開來,這裏主要討論原型對象 prototype
就不闡述關於原型 __proto__
的問題了,若是有疑惑的話,能夠閱讀 @nightire 凡哥的博文《理解 JavaScript(四)》。
http://bonsaiden.github.io/JavaScript-Garden/#object.prototype