繼承的七種方式
1. 原型鍊繼承
-
原理:
即将子類構造函數所對應的的原型對象即 prototype 這個對象(即是一個引用)指向
改變,将其指向改為指向父類建立的執行個體(因為建立的執行個體中有一個屬性[[prototype]]實際上在現代浏覽器上已經實作即__proto__)
是指向父類構造函數對應的原型對象。實作原型鍊的繼承。
- 實作代碼
SubType.prototype = new super(); SubType.prototype.constructor = SubType;
-
缺點:
實作繼承了父類的原型對象上定義的屬性和方法,而使用子類建立的執行個體都共享這一塊資料。
2 . 構造函數繼承
-
原理:
在子類的構造函數中調用父類的構造函數實作構造函數的繼承
-
實作
在子類的構造函數中調用父類構造函數實作繼承父類屬性
-
缺點
雖然實作了屬性的繼承,但是方法不是共享的,沒建立一個子類執行個體就單獨建立了一個方法,沒有實作
方法之間的共享。
3 . 組合繼承
-
原理
采用構造函數繼承實作構造函數的繼承(即實作父類構造函數的繼承),另改變原型鍊的指向使原型執行指向父類的原型對象實作方法即共享屬性的繼承實作。
- 實作
//首先在子類的構造函數中調用父類的構造函數 SuperType.call(this); //其次在外面實作原型鍊 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType;
-
要點
最為經典的繼承方式實作了完全的繼承,但是存在父類的構造函數調用了兩次的現象,即一次是在原型指向父類的執行個體對象時調用了一次父類,另一次是在子類的構造函數中顯式調用的父類的構造函數。
4 . 原型式繼承
-
原理
借助原型可以基于已有的對象建立新的對象,即一個方法實作建立傳回一個子類的的執行個體,并将建立這個子類的構造函數對應的原型對象改變成傳入的對象,實作繼承傳入的對象的基本原理–在 ES5 時實作了這個方法
;Object.create(o)
- 實作
function object(o) { function F() {} F.prototype = o; return new F(); } //實作繼承 var person = { name: "mumu", friends: ["my", "andy"] }; var anotherPerson = object(person); anotherPerson.name = "greny"; anotherPerson.friends.push("yxiuchao"); console.log(anotherPerson.__proto__); //{ name: 'mumu', friends: [ 'my', 'andy', 'yxiuchao' ] }
-
要點
實作了原型屬性及方法的繼承,但是當多個使用此方法建立的對象當操作一個引用類型時,由于操作的引用類型存儲的資料指向的位址單元是相同的,故無法進行函數的複用。
5 . 寄生式繼承
-
原理
寄生式繼承是與原型式繼承緊密相關的一種思路,思路是與寄生構造函數和工廠模式類似,即建立一個僅用于封裝繼承過程的函數。
- 實作
其中既有基于function createAnothor(original) { var clone = object(original); //通過調用函數建立一個對象 clone.sayHi = function() { //給這個對象建立函數--增強這個對象 console.log("hi"); }; return clone; }
的屬性和方法又有自己的original
方法sayHi
-
要點
使用寄生式繼承來為對象添加函數,會由于不能做到函數複用而降低效率;這一點與構造函數模式類似。
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"]; } SuperType.prototype.sayHi = function() { console.log(this.name); }; function SubType(name, age) { SuperType.call(this, name); //調用構造函數 this.age = age; } inheritPrototype(SubType, SuperType); //實作原型鍊的混合繼承---繼承原型的副本
-
要點
調用父類的構造函數一次,且子類的原型是執行父類的原型上。實作真正的繼承
7 . ES6 中的 class 繼承
差別:
ES6 中的 class 實作類 extends 繼承實際上是和寄生組合式繼承一樣,同樣隻是調用了一遍父類的構造函數,唯一差別是,子類建立時 this 的變化,使用 ES6 的繼承是先建立父類的 this 在建立子類的 this(
在子類繼承時必須先調用super()
),而寄生組合式繼承是直接建立子類的 this。