JavaScript常用七種繼承方案
建構函式、原型和例項之間的關係:每個建構函式都有一個原型物件,原型物件都包含一個指向建構函式的指標,而例項都包含一個原型物件的指標。
繼承的本質是 重寫原型物件,代之以一個新型別的例項 。
function SuperType() { this.property = true; } SuperType.prototype.getSuperValue = function() { return this.property; } function SubType() { this.subproperty = false; } // 這裡是關鍵,建立SuperType的例項,並將該例項賦值給SubType.prototype SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function() { return this.subproperty; } var instance = new SubType(); console.log(instance.getSuperValue()); // true 複製程式碼

原型鏈方案存在的缺點:多個例項對引用型別的操作會被篡改。
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){} SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green,black" 複製程式碼
2、借用建構函式繼承
使用父類的建構函式來增強子類 例項 ,等同於複製父類的例項給子類(不使用原型)
functionSuperType(){ this.color=["red","green","blue"]; } functionSubType(){ //繼承自SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.color.push("black"); alert(instance1.color);//"red,green,blue,black" var instance2 = new SubType(); alert(instance2.color);//"red,green,blue" 複製程式碼
核心程式碼是 SuperType.call(this)
,建立子類例項時呼叫 SuperType
建構函式,於是 SubType
的每個例項都會將SuperType中的屬性複製一份。
缺點:
- 只能繼承父類的 例項 屬性和方法,不能繼承原型屬性/方法
- 無法實現複用,每個子類都有父類例項函式的副本,影響效能
3、組合繼承
組合上述兩種方法就是組合繼承。用原型鏈實現對 原型 屬性和方法的繼承,用借用建構函式技術來實現 例項 屬性的繼承。
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ // 繼承屬性 // 第二次呼叫SuperType() SuperType.call(this, name); this.age = age; } // 繼承方法 // 構建原型鏈 // 第一次呼叫SuperType() SubType.prototype = new SuperType(); // 重寫SubType.prototype的constructor屬性,指向自己的建構函式SubType SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27 複製程式碼

缺點:
- 第一次呼叫
SuperType()
:給SubType.prototype
寫入兩個屬性name,color。 - 第二次呼叫
SuperType()
:給instance1
寫入兩個屬性name,color。
例項物件 instance1
上的兩個屬性就遮蔽了其原型物件SubType.prototype的兩個同名屬性。所以,組合模式的缺點就是在使用子類建立例項物件時,其原型中會存在兩份相同的屬性/方法。
4、原型式繼承
利用一個空物件作為中介,將某個物件直接賦值給空物件建構函式的原型。
function object(obj){ function F(){} F.prototype = obj; return new F(); } 複製程式碼
object()對傳入其中的物件執行了一次 淺複製
,將建構函式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" 複製程式碼
缺點:
- 原型鏈繼承多個例項的引用型別屬性指向相同,存在篡改的可能。
- 無法傳遞引數
另外,ES5中存在 Object.create()
的方法,能夠代替上面的object方法。
5、寄生式繼承
核心:在原型式繼承的基礎上,增強物件,返回建構函式
function createAnother(original){ var clone = object(original); // 通過呼叫 object() 函式建立一個新物件 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.create(superType.prototype); // 建立物件,建立父類原型的一個副本 prototype.constructor = subType;// 增強物件,彌補因重寫原型而失去的預設的constructor 屬性 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); } var instance1 = new SubType("xyc", 23); var instance2 = new SubType("lxy", 23); instance1.colors.push("2"); // ["red", "blue", "green", "2"] instance1.colors.push("3"); // ["red", "blue", "green", "3"] 複製程式碼

這個例子的高效率體現在它只調用了一次 SuperType
建構函式,並且因此避免了在 SubType.prototype
上建立不必要的、多餘的屬性。於此同時,原型鏈還能保持不變;因此,還能夠正常使用 instanceof
和 isPrototypeOf()
這是最成熟的方法,也是現在庫實現的方法
7、混入方式繼承多個物件
function MyClass() { SuperClass.call(this); OtherSuperClass.call(this); } // 繼承一個類 MyClass.prototype = Object.create(SuperClass.prototype); // 混合其它 Object.assign(MyClass.prototype, OtherSuperClass.prototype); // 重新指定constructor MyClass.prototype.constructor = MyClass; MyClass.prototype.myMethod = function() { // do something }; 複製程式碼
Object.assign
會把 OtherSuperClass
原型上的函式拷貝到 MyClass
原型上,使 MyClass 的所有例項都可用 OtherSuperClass 的方法。
ofollow,noindex">《javascript高階程式設計》筆記:繼承
JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fcreate" rel="nofollow,noindex">Object.create()