1 原型鏈繼承
核心:讓子類的原型指向父類的實例
ChildType.prototype = new ParentType() // 所有涉及到原型鏈繼承的繼承方式都要修改子類構造函數的指向,否則子類實例的構造函數會指向PerentType。
ChildType.prototype.constructor =ChildType;
優點:父類方法可以復用
缺點:
- 父類的引用屬性會被所有子類實例共享
- 子類構建實例時不能向父類傳遞參數
2 .構造函數繼承
核心:將父類構造函數的內容復制給了子類的構造函數。這是所有繼承中唯一一個不涉及到prototype的繼承。
ParentType.call(ChildType);
優點:和原型鏈繼承完全反過來。
- 父類的引用屬性不會被共享
- 子類構建實例時可以向父類傳遞參數
缺點:父類的方法不能復用,子類實例的方法每次都是單獨創建的。
3 .組合繼承
核心:原型式繼承和構造函數繼承的組合,兼具了二者的優點。
function ParentType() { this.name = 'parent'; this.arr = [1, 2, 3]; }
ParentType.prototype.say = function() { console.log('this is parent') }
function ChildType() {
ParentType.call(this)
// 第二次調用ParentType
}
ChildType.prototype = new ParentType() // 第一次調用ParentType
優點:
- 父類的方法可以被復用
- 父類的引用屬性不會被共享
- 子類構建實例時可以向父類傳遞參數
缺點:
調用了兩次父類的構造函數,第一次給子類的原型添加了父類的name, arr屬性,第二次又給子類的構造函數添加了父類的name, arr屬性,從而覆蓋了子類原型中的同名參數。這種被覆蓋的情況造成了性能上的浪費。
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"
上文中代碼可以轉變為
var yetAnotherPerson = object(person); => var yetAnotherPerson = Object.create(person);
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"
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);
優缺點:這是一種完美的繼承方式。
7:ES6 Class extends
核心: ES6繼承的結果和寄生組合繼承相似,本質上,ES6繼承是一種語法糖。但是,寄生組合繼承是先創建子類實例this對象,然后再對其增強;而ES6先將父類實例對象的屬性和方法,加到this上面(所以必須先調用super方法),然后再用子類的構造函數修改this。
class A {}
class B extends A {
constructor() {
super();
}
}
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中不是。