物件的繼承
- 詳細內容
- 分類:Javascript
- 發佈:2013-07-22, 週一 23:14
- 點擊數:2200
物件的繼承:
繼承的目的在於重用程式碼,以及結構化的資料。在程式中,物件通用的屬性與方法,都應該用繼承的方式來進行,這是一種比較有效率的做法。
Javascript的物件繼承原理其實並不難,只是相當異於一般(基於類別)的物件導向程式。Javascript是一個基於原型的物件導向程式,他的繼承主要就是依據原型來實作。如果你學過其他的物件導向語言,可能會有點不習慣,不過沒關係,先將其他的物件導向語言的概念放一旁。重新學學Javascript的繼承方式。
Javascript的繼承作法很簡單,就是將要被繼承的"物件"指定給prototype屬性就可以了。不過這屬性不是物件本身的屬性,而是建構函式的屬性。將物件指定給建構函式的屬性,然後由這建構函式所建立的物件,都會繼承該物件的屬性。通常我們可以把要被繼承的屬性寫在一個物件實字,然後指定給建構函式的prototype。
<script type="text/javascript"> function creature(name){ //建構函式 this.name=name.toString(); } creature.prototype={ //將物件實字指定給建構函式的prototype屬性。 setname:function(name){ this.name=name.toString();}, getname:function(){ return this.name;}, } var mycreature=new creature("維克"); alert("姓名: "+mycreature.getname());//mycreature繼承了物件實字中的getname()方法。 </script>
上例的程式碼中凡是藉由creature建構函式所建立的物件,都會繼承setname()與getname()兩個方法。這兩個方法當然也可以寫在建構函式內,不過這會導致每次建立物件時都要實作一次方法。不是一種有效率的做法。要記住,通用的屬性與方法,應該要寫在原型當中。那把name屬性也寫進原型中呢?將name屬性寫在原型中得小心一些狀況,就是,他其實可能會被不小心覆蓋掉,你用不到他,他卻占著記憶體。下面例子我們試著把name寫在prototype中看看。
<script type="text/javascript"> function creature(name){ //建構函式 } creature.prototype={ //將物件實字指定給建構函式的prototype屬性。 name:"豬頭", setname:function(name){ this.name=name.toString();}, getname:function(){ return this.name;}, } var mycreature=new creature("維克"); alert("姓名: "+mycreature.getname());//輸出 豬頭 mycreature.setname("維克"); alert("姓名: "+mycreature.getname());//輸出 維克 alert("姓名: "+creature.prototype.getname());//輸出 豬頭 </script>
上例原型中有一個name屬性,因此在物件mycreature新建立時,getname()方法可以讀取到name屬性,此時值為"豬頭"。但是當呼叫setname("維克")方法時,由於函式內的this會指向mycreature物件,而不是原型物件,所以會在mycreature物件中建立一個新的屬性name,其值會等於"維克",而原型物件中的name屬性並未被更改,所以再次呼叫getname()方法時會傳回mycreature物件的name屬性。換一種狀況,當你企圖在建構函式中初始化name時,其實就已經是建立了另一個name了。
function creature(name){ //建構函式 this.name=name.toString(); //這會為物件建立一個name屬性,因而覆蓋掉原型中的name屬性,注意不能在這裡呼叫setname()。 }
我們用圖形解釋一下這狀況:
在存取物件屬性時,會先從最物件本身開始,再往其原型逐漸查詢過去,原型找不到就繼續找原型的原型,一直持續下去直到沒有原型為止,這種行為稱之為原型鍊。
圖中的 __proto__ 屬性會指向該物件的原型。不過這不是個標準屬性 (至少目前ECMA5還未定義該參數。),並不是每個瀏覽器都有這屬性,不過這並不妨礙我們用它來解釋繼承關係。
原型的私有屬性:
上面原型中的name屬性是公開屬性,我們來看看私有屬性會有什麼特性,我們利用閉包來產生私有屬性:
<script type="text/javascript"> function creature(name){ //建構函式 this.name=name.toString(); this.creature_type="精靈"; } creature.prototype=(function(){//立即函數傳回物件 包裹私有變數 產生閉包 var creature_type="人類"; //要記得使用var 不然會變成全域變數 return { //傳回物件給creature.prototype settype:function(type){ creature_type=type.toString();},//注意這裡並沒有使用this gettype:function(){ return creature_type;},//注意這裡並沒有使用this };})(); var mycreature=new creature("維克"); alert("種族: "+mycreature.gettype());//輸出 人類 mycreature.settype("獸人"); alert("種族: "+mycreature.gettype());//輸出 獸人 alert("種族: "+creature.prototype.gettype());//輸出 獸人 </script>
creature_type是原型中的一個私有屬性,當呼叫settype()時並沒有使用this,因此不會在物件本身建立另一個creature_type屬性。而且既便是在建構函式中直接為物件建立一個creature_type屬性,也不會影響到gettype()方法傳回的值,因為gettype中傳回的是原型的私有屬性。
共享的屬性與方法:
在原型中的屬性與方法,不論是公開或是私有,都是在物件中共享的,在上例子,凡是藉由creature()建構函式建構出來的物件,都會共用原型中的屬性與方法。也就是說一個物件使用settype()方法改變原型中creature_type的值,另一個物件在可以在使用gettype()時讀出改變後的值。這道理並不難,畢竟它們的原型其實就是同一個物件。
其他繼承模式:
上面解釋了prototype繼承的基本概念,或許這上面提到的特性並無法滿足你的需求,不過沒關係,Javascript的自由度造就了許多不同的繼承模型。或許有其他模型能滿足你的需求。我們後續再提。
按個讚!~支持本站!~