原文地址:JavaScript實現繼承javascript
衆所周知,JavaScript
這門語言在 ES6
出來以前是沒有類(class)這一律唸的,因此 JavaScript
中的類都是經過原型鏈來實現的。一樣,使用 JavaScript
也能實現面向對象的實現繼承。如下是《高程》(第三版)的讀書筆記。java
經過原型鏈實現繼承很容易理解,也很簡單。將子類的原型指向父類的實例便可。寫個簡單的例子:git
// 父類 function SuperType() { this.property = "super"; } // 在父類的原型上定義方法 SuperType.prototype.getSuperVal = function () { console.log(this.property); }; // 子類 function SubType() { this.property = "sub"; } // 子類繼承父類 SubType.prototype = new SuperType(); var instance = new SubType(); console.log(instance.getSuperVal()); // "sub"
對於子類來說,其原型的指向應該是這樣的:SubType -> new SuperType() -> SuperType.prototype -> Object.prototype -> null
。github
注意:函數
原型鏈的問題:this
這個方法是爲了解決原型鏈方式帶來的問題,使用十分巧妙,利用了 call
方法。代碼實現:prototype
// 父類 function SuperType() { this.users = ["Jack", "Tom"]; } // 子類 function SubType() { // 繼承 SuperType.call(this); } var instance1 = new SubType(); var instance2 = new SubType(); instance1.users.pop(); // "Tom" console.log(instance2.users); // ["Jack", "Tom"]
經過借用構造函數解決了共享原型屬性致使的問題。同時也能夠經過 call
方法給父類傳遞參數。設計
借用構造函數的問題:code
組合繼承有時也叫僞經典繼承,該繼承模式將原型鏈和借用構造函數的技術結合在一塊兒實現。示例代碼:對象
// 父類 function SuperType(company) { this.company = company; this.staffs = ["Jack", "Tom"]; } // 父類方法 SuperType.prototype.getCompany = function () { console.log(this.company); }; // 子類 function SubType(company, product) { // 繼承屬性 SuperType.call(this, company); this.product = product; } // 繼承方法 SubType.prototype = new SuperType(); // 指向正確的constructor SubType.prototype.constructor = SubType; SubType.prototype.getProduct = function () { console.log(this.product); }; // SubType實例 var instance1 = new SubType("A", "tellphone"); instance1.getCompany(); // "A" instance1.getProduct(); // "tellphone" instance1.staffs.push("Amy"); // ["Jack", "Tom", "Amy"] var instance2 = new SubType("B", "toy"); instance2.getCompany(); // "B" instance2.getProduct(); // "toy" console.log(instance2.staffs); // ["Jack", "Tom"]
從代碼的例子能夠觀察到,組合繼承模式可讓子類的多個實例既能擁有本身的屬性,又能使用相同的方法,融合了原型鏈和借用構造函數的優勢。
原型式繼承是藉助原型能夠基於已有的對象建立新對象,同時還沒必要所以建立自定義類型。使用以下函數實現:
function object(obj) { function F() {} F.prototype = obj; return new F(); }
object
函數對傳入的對象實現了淺複製。因此對全部由其建立的實例都共享了 obj
對象中的引用屬性。
寄生式繼承模式和原型式繼承模式很類似,建立了一個僅用於封裝繼承過程的函數,在函數內部加強對象的功能:
function createAnother(obj) { var clone = object(obj); clone.saySomething = function () { alert("Hello world!"); }; return clone; } function object(obj) { function F() {} F.prototype = obj; return new F(); }
經過 createAnother
函數,對對象的功能進行加強,然而這種方式也沒有達到函數複用的效果,這一點和構造函數模式同樣。
經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法。寄生組合模式使用寄生模式來實現對父類原型的繼承,再將結果指定給子類的原型。其基本模式以下:
function inheritPrototype(subType, superType) { // 返回父類原型副本並賦值給子類原型 subType.prototype = object(superType.prototype); subType.prototype.constructor = subType; }
再來看一個例子:
// 父類 function SuperType(name) { this.name = name; } // 在父類的原型上定義方法 SuperType.prototype.getName = function () { console.log(this.name); }; // 子類 function SubType(name, age) { SuperType.call(this, name); this.age = age; } // 寄生組合繼承 inheritPrototype(SubType, SuperType); // 添加子類方法 SubType.prototype.getAge = function () { console.log(this.age); };
和組合繼承模式相比,寄生組合式繼承模式只調用了一次 SuperType
構造函數,也避免了在 SubType.prototype
上建立多餘的屬性。開發人員廣泛認爲寄生組合式繼承是引用類型最理想的繼承範式。
《JavaScript高級程序設計》(第三版)