首頁  >  文章  >  web前端  >  JavaScript物件導向-基於函數偽造的方式實作繼承

JavaScript物件導向-基於函數偽造的方式實作繼承

黄舟
黄舟原創
2017-01-19 15:24:431415瀏覽

由於基於原型鏈實現繼承的方式存在一些缺點,所以人們採用了另一種方式來實現繼承——基於函數偽造的方式實現繼承。這個技術的想法是在子類別建構子的內部呼叫父類別的建構子。

 基於函數偽造的方式實現繼承的實作方式

因為在JavaScript中,函數是在特定環境中執行程式碼的對象,所以我們可以使用call()或apply()方法來在子類別物件上執行父類別物件的建構函數。來看下面的範例:

/* 创建父类 */
function Parent(){
  this.color = ["red","blue"];
}
 
/* 创建子类 */
function Child(){
  // 继承父类的属性
  Parent.call(this);
}

在上面的程式碼中,我們先建立了一個父類別Parent,然後建立一個子類別Child,並在子類別的內部使用Parent.call(this);來完成繼承。

在函數的屬性一文中,我們已經介紹了call()和apply()方法,這兩個方法的作用是在特定的作用域中呼叫函數,也就是說這兩個方法可以透過函數名稱來呼叫函數。這裡我們在Child的內部使用Parent.call(this);來完成繼承,這句話的意思是在子類別中呼叫父類別的建構函數,此時的this指的是Child物件(在Child中的this應該是執行Child的物件),所以就等於在Child中有了一句this.color = ["red","blue"];,也就是等於在Child中有了this.color屬性,這樣也就變相的完成了繼承。

我們可以透過下面的方法來進行驗證:

var c1 = new Child();     //创建子类对象c1
c1.color.push("Green");   //为c1添加新的颜色
console.info(c1.color);   //控制台输出:red,blue,Green
 
var c2 = new Child();     //创建子类对象c2
console.info(c2.color);   //控制台输出:red,blue

在上面的程式碼中,我們創建了子類物件c1,並為它添加新的顏色」Green“,所以會在控制台中輸出:"red, blue,Green"。然後我們又建立了物件c2,因為沒有為它添加新的顏色,所以它只會在控制台中輸出繼承自父類別的顏色:"red,blue"。

每調用一次new Child就等於執行了一次物件屬性的設定,此時,每個物件的空間中都有color屬性,而不會在原型中存在,所以color不會被共享。這樣就解決了原型鏈繼承中引用型別變數存在的問題。

 子類別建構子

原型鏈繼承的另一個缺點是無法從子類別中呼叫父類別的建構函數,這樣就沒有辦法把子類別中的屬性賦值到父類別中。透過函數偽造的方式可以很好的解決這個問題。來看下面的範例:

// 创建父类
function Parent(name){
  this.name = name;
}
 
//创建子类
function Student(name,age){
  //使用伪造的方式就可以把子类的构造函数参数传递到父类中
  Parent.call(this,name); //调用父类的属性
  this.age = age;
}
 
var s1 = new Student("Leon",22);
var s2 = new Student("Ada",25);
 
console.info(s1.name + "," + s1.age);  // 控制台输出:Leon,22
console.info(s2.name + "," + s2.age);  // 控制台输出:Ada,25

在上面的程式碼中,子類別Student透過函數偽造的方式呼叫父類別的name屬性,實際上是為子類別新增一個name屬性。在這裡,call()方法將Student類別的參數name傳遞到父類別中,完成的操作相當於this.name = name;。而這個name屬性是子類別的name屬性,而不是父類別的name屬性。

 基於函數偽造實作繼承存在的問題

在上面的討論中,我們講的只是子類別繼承父類別的屬性,那麼子類別如何繼承父類別的方法呢?在前面我們說過,通常我們將方法放到原型中設置,例如父類中有一個say()方法,程式碼如下:

// 创建父类
function Parent(name){
  this.name = name;
}
 
// 父类的say()方法
Parent.prototype.say = function(){
  console.info(this.name);
}
 
//创建子类
function Student(name,age){
  Parent.call(this,name); 
  this.age = age;
}

由於使用函數偽造的方式不會完成子類Student的原型指向父類Parent,所以子類別繼承父類別之後,say()方法是不存在的。解決這個問題的方法是,將say()方法放置到Parent中使用this關鍵字來創建。

// 创建父类
function Parent(name){
  this.name = name;
  // 父类的say()方法
  this.say = function(){
    console.info(this.name);
  }
}
 
//创建子类
function Student(name,age){
  Parent.call(this,name); 
  this.age = age;
}

這樣做雖然可以使子類別繼承父類別的say()方法,但是又產生了另外一個問題:每次創建子類別物件的時候都會產生一個say()方法,會佔用大量的記憶體空間。

由於基於函數偽造的方式實現繼承也存在缺陷,所以我們也不會單獨使用這種方式來完成繼承,而是會使用基於組合的方式實現繼承,我們將在下一篇文章中介紹這種繼承方式。

以上就是JavaScript物件導向-基於函數偽造的方式實現繼承的內容,更多相關內容請關注PHP中文網(www.php.cn)!


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