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