本文以《JavaScript高級程序設計》上的內容爲骨架,補充了ES6 Class的相關內容,從我認爲更容易理解的角度將繼承這件事敘述出來,但願你們能有所收穫。前端
其中,原型鏈繼承和原型式繼承有同樣的優缺點,構造函數繼承與寄生式繼承也相互對應。寄生組合繼承基於Object.create, 同時優化了組合繼承,成爲了完美的繼承方式。ES6 Class Extends的結果與寄生組合繼承基本一致,可是實現方案又略有不一樣。web
下面立刻進入正題。編程
2.1 原型鏈繼承設計模式
核心:將父類的實例做爲子類的原型bash
SubType.prototype = new SuperType()
// 全部涉及到原型鏈繼承的繼承方式都要修改子類構造函數的指向,不然子類實例的構造函數會指向SuperType。
SubType.prototype.constructor = SubType;
複製代碼
優勢:父類方法能夠複用函數
缺點:性能
父類的引用屬性會被全部子類實例共享 子類構建實例時不能向父類傳遞參數學習
2.2 構造函數繼承優化
核心:將父類構造函數的內容複製給了子類的構造函數。這是全部繼承中惟一一個不涉及到prototype的繼承。ui
SuperType.call(SubType);
複製代碼
優勢:和原型鏈繼承徹底反過來。
父類的引用屬性不會被共享 子類構建實例時能夠向父類傳遞參數 缺點:父類的方法不能複用,子類實例的方法每次都是單首創建的。
2.3 組合繼承
核心:原型式繼承和構造函數繼承的組合,兼具了兩者的優勢。
function SuperType() {
this.name = 'parent';
this.arr = [1, 2, 3];
}
SuperType.prototype.say = function() {
console.log('this is parent')
}
function SubType() {
SuperType.call(this) // 第二次調用SuperType
}
SubType.prototype = new SuperType() // 第一次調用SuperType
複製代碼
優勢:
父類的方法能夠被複用 父類的引用屬性不會被共享 子類構建實例時能夠向父類傳遞參數 缺點: 調用了兩次父類的構造函數,第一次給子類的原型添加了父類的name, arr屬性,第二次又給子類的構造函數添加了父類的name, arr屬性,從而覆蓋了子類原型中的同名參數。這種被覆蓋的狀況形成了性能上的浪費。
2.4 原型式繼承
核心:原型式繼承的object方法本質上是對參數對象的一個淺複製。 優勢:父類方法能夠複用 缺點:
父類的引用屬性會被全部子類實例共享 子類構建實例時不能向父類傳遞參數
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
複製代碼
ECMAScript 5 經過新增 Object.create()方法規範化了原型式繼承。這個方法接收兩個參數:一 個用做新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。在傳入一個參數的狀況下, Object.create()與 object()方法的行爲相同。——《JAVASCript高級編程》
因此上文中代碼能夠轉變爲
var yetAnotherPerson = object(person); => var yetAnotherPerson = Object.create(person);
複製代碼
2.5 寄生式繼承
核心:使用原型式繼承得到一個目標對象的淺複製,而後加強這個淺複製的能力。 優缺點:僅提供一種思路,沒什麼優勢
function createAnother(original){
var clone=object(original); //經過調用函數建立一個新對象
clone.sayHi = function(){ //以某種方式來加強這個對象
alert("hi");
};
return clone; //返回這個對象
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
複製代碼
2.6 寄生組合繼承 剛纔說到組合繼承有一個會兩次調用父類的構造函數形成浪費的缺點,寄生組合繼承就能夠解決這個問題。
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); // 建立了父類原型的淺複製
prototype.constructor = subType; // 修正原型的構造函數
subType.prototype = prototype; // 將子類的原型替換爲這個原型
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
// 核心:由於是對父類原型的複製,因此不包含父類的構造函數,也就不會調用兩次父類的構造函數形成浪費
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}
複製代碼
優缺點:這是一種完美的繼承方式。
2.7 ES6 Class extends
核心: ES6繼承的結果和寄生組合繼承類似,本質上,ES6繼承是一種語法糖。可是,寄生組合繼承是先建立子類實例this對象,而後再對其加強;而ES6先將父類實例對象的屬性和方法,加到this上面(因此必須先調用super方法),而後再用子類的構造函數修改this。
class A {}
class B extends A {
constructor() {
super();
}
}
複製代碼
ES6實現繼承的具體原理:
class A {
}
class B {
}
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
// B 的實例繼承 A 的實例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 繼承 A 的靜態屬性
Object.setPrototypeOf(B, A);
複製代碼
ES6繼承與ES5繼承的異同: 相同點:本質上ES6繼承是ES5繼承的語法糖 不一樣點:
ES6繼承中子類的構造函數的原型鏈指向父類的構造函數,ES5中使用的是構造函數複製,沒有原型鏈指向。 ES6子類實例的構建,基於父類實例,ES5中不是。
3. 總結
ES6 Class extends是ES5繼承的語法糖 JS的繼承除了構造函數繼承以外都基於原型鏈構建的 能夠用寄生組合繼承實現ES6 Class extends,可是仍是會有細微的差異
這裏推薦一下個人前端學習交流羣:784783012 ,裏面都是學習前端的,若是你想製做酷炫的網頁,想學習編程。本身整理了一份2018最全面前端學習資料,從最基礎的HTML+CSS+JS【炫酷特效,遊戲,插件封裝,設計模式】到移動端HTML5的項目實戰的學習資料都有整理,送給每一位前端小夥伴,有想學習web前端的,或是轉行,或是大學生,還有工做中想提高本身能力的,正在學習的小夥伴歡迎加入學習。