首頁  >  文章  >  web前端  >  js類別繼承的具體實作方法_基礎知識

js類別繼承的具體實作方法_基礎知識

WBOY
WBOY原創
2016-05-16 17:06:06908瀏覽

在開始擺弄程式碼之前,應該先搞清楚使用繼承的目的和能帶來什麼好處。一般來說,在設計類別的時候,我們希望能減少重複性的程式碼,並且盡量弱化類別之間的耦合。而要做到這兩者都兼顧是很難的,我們需要根據具體的條件和環境來決定我們應該採取什麼方法。根據我們對物件導向語言中繼承的了解,繼承會帶類別直接的強耦合,但js由於其特有的靈活性,可以設計出強耦合和弱耦合,高效率和低效率的程式碼。而具體用什麼,看情況。

下面提供js實作繼承的三種方法:類別式繼承,原型繼承,摻元類別。這裡先簡述類式繼承,後兩種在往後的隨便中簡述,請多多關注、指導,謝謝。

類別式繼承。

js類別式繼承的實作是依靠原型鏈來實現的。什麼是原型鏈? js中物件有個屬性prototy,這個屬性回傳物件類型的引用,用來提供物件的類別的一組基本功能。

似乎對prototype有印象,對了,我們常這樣用程式碼。

複製程式碼 程式碼如下:

var Person = function(){ name = "liyatang";
};
Person.prototype = {
    //在這裡提供Person的基本功能
    getName : function(){
  🎜>    }
}


我們把類別的基本功能放在prototype屬性裡,表示Person這個物件的引用有XXX功能。

在理解原型後,需要理解下什麼是原型鏈。在存取對象的某個成員(屬性或方法)時,如果這個成員未見於當前對象,那麼js會在prototype屬性所指的那個對像中查找它,如果還沒有找到,就繼續到下一級的prototype所指的對像中查找,直至找到。如果沒有找到就會回傳undifined。

那麼原型鏈給我們什麼提示呢?很容易聯想到,原型鏈意味著讓一個類別繼承另一個類,只需將子類的prototype設定為指向父類的一個實例即可。這就把父類別的成員綁定到子類別上了,因為在子類別上查找不到某個成員時會往父類別中尋找。 (以上這兩段用詞不嚴謹,只在用簡單易懂的言語描述)

下面我們需要個Chinese類,需要繼承Person類別的name和getName成員。

複製程式碼 程式碼如下:
varese = function(name,nation){

varese = function(name, nation) >        //繼承,且需要呼叫父類別的建構函數,可用call調用,this指向Chinese
    //使Person在此作用域上,才可呼叫Person的成員
    Person.call(this,name) );
    this.nation = nation;
};
Chinese.prototype = Person.prototype;
//這裡不可跟以前一樣,因為覆掉了prototype屬性
//Chinese .prototype = {
//  getNation : function(){
//      return this.nation;
//  }
//};
//以後的方法都需要這樣加裝
Chinese.prototype.getNation = function(){
        return this.nation;
};

繼承關係就建立了,我們這樣呼叫它

複製程式碼 程式碼如下:
var c = new Chinese("liyatang","China ");
alert(c.getName());// liyatang

於是類別繼承就這樣完成了。真的完成了嘛,用firebug在alert那裡設斷點,會發現原來的Person.prototype被修改了,加入了getNation方法。


js類別繼承的具體實作方法_基礎知識

這是因為在上面的程式碼Chinese.prototype = Person.prototype; 這是引用型,修改Chinese同時也修改了Person。這本身就是不能容忍的,且使類別之間形成強耦合性,這不是我們要的效果。

我們可以另一個物件或實例化一個實例來弱化耦合性。


複製程式碼 程式碼如下:
//第一種
//Chinese. prototype = new Person();
//第二種
//var F = function(){};
//F.prototype = Person.prototype;
//Chinese.prototype = F.prototype;

這兩種方法有什麼差別呢。在第二種中加入了一個空函數F,這樣做可以避免創建父類的一個實例,因為有可能父類會比較龐大,而且父類的構造函數會有一些副作用,或者說會執行大量的計算任務。所以力薦第二種方法。

到此,完了嘛,還沒!在物件的屬性prototype下面有個屬性constructor,它保存了對建構特定物件實例的函數的參考。根據這個說法Chiese.prototype.constructor應該等於Chinese,實際上不是。

回憶之前在設定Chiese的原型鏈時,我們把Person.prototype 覆掉了Chiese.prototype。所以此時的Chiese.prototype.constructor是Person。我們還需要加入以下程式碼

複製程式碼 程式碼如下:

//對這裡的if條件不需要細究,知道Chinese.prototype.constructor = Chinese就行
if(Chinese.prototype.constructor == Object.prototype.constructor){
    Chinese.prototype.constructor = Chinese;
}
}
> 整理全部程式碼如下


複製程式碼 程式碼如下:
var Person = function( name){
    this.name = name;
};
Person.prototype = {
    getName : function(){
 ;

var Chinese = function(name, nation){
    Person.call(this,name);
    this.nation = nation;};
var F = function🎜>};
var F = function🎜>};
var F = function🎜>};
var F = function (){};
F.prototype = Person.prototype;
Chinese.prototype = F.prototype;
if(Chinese.prototype.constructor == Object.prototype.constructor){
 .prototype.constructor = Chinese;
}
Chinese.prototype.getNation = function(){
        return this.nation;
Chin};
;alert(c.getName());


如果可以把繼承的程式碼放在一個函數裡,方便程式碼重複使用,最後整理程式碼如下

複製程式碼 程式碼如下:
function extend(subClass,>

function extend(subClass,🎜>

function extend(subClass, superClass) F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor =subClass;
    subClass.prototype.constructor =subClass;    if(superClass.prototype.constructor == Object.prototype.constructor){
     🎜>    }
}

var Person = function(name){
    this.name = name;
};Person.prototype = {        return this.name;
    }
};

var Chinese = function(name, nation){

var Chinese = function(name, nation){

var Chinese = function(name, nation){ this.nation = nation;
};
extend(Chinese, Person);
Chinese.prototype.getNation = function(){
      
var c = new Chinese("liyatang","China");
alert(c.getName());


發表後修改:

在一樓的評論下,我對那個extend函數又有新的看法。之前在討論如何設定原型鏈時提出了兩種方法

複製程式碼 程式碼如下:
//第一種
//Chinese. prototype = new Person();
//第二種
//var F = function(){};
//F.prototype = Person.prototype;
//Chinese.prototype = F.prototype;

雖然第二種減少了呼叫父類別的建構子這條路,但在設計Chinese類別時用了Person.call(this,name);這裡也相當於呼叫了父類別的建構子。

然而用第一種方法的話可以減少在Chinese中再寫Person.call(this,name);,這部分程式碼在子類別中往往會被遺忘。不妨把這種功能程式碼放在了extend裡。就只寫

Chinese.prototype = new Person();也達到同樣的目的:耦合不強。

但遺忘的一點是,Chinese.prototype = new Person();這樣寫對嘛。答案是不對!很明顯 new Person()需要傳一個name參數的。我們不可能在extend函數裡做這部分工作,只好在Chinese類別裡呼叫父類別的建構子了。這樣也符合物件導向的思路。

所以,還是力薦用第二種方法。

第一次這樣寫有關技術類的文章,基本是按自己的思路鋪展開來,難免會有一些沒有考慮到的地方和解釋的不清楚的地方,望留言反饋,謝謝。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn