原型鏈繼承
原型鏈繼承是ECMAScript的主要繼承方式。其基本思想就是通過原型繼承多個引用類型的屬性和方法。什么是原型鏈?每個構造函數都會有一個原型對象,調用構造函數創建的實例會有一個指針__proto__指向原型對象,這個原型可能是另一個類型的實例,所以內部可能也有一個指針指向另一個原型,然后就這樣形成了一條原型鏈。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
代碼: function SuperType() { this .property = true ; } SuperType.prototype.getSuperValue = function () { return this .property; }; function SubType() { this .subproperty = false ; } // 繼承SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function () { //注意 不能通過對象字面量的方式添加新方法,否則上一行無效 return this .subproperty; }; let instance = new SubType(); console.log(instance.getSuperValue()); // true |
缺點
1.如果父類實例的屬性是引用類型的時候,其實父類的實例屬性會成為子類的原型屬性,子類創建的所有實例都會共享這些方法,修改一個實例的這個屬性,其他實例的屬性也會被修改
2.子類型在實例化時不能給父類型的構造函數傳參
構造函數繼承
為了解決原型包含引用值導致的繼承問題,出現了一中'盜用構造函數'的技術流行起來,也被稱為'對象偽裝'或'經典繼承',思路就是在子類構造函
數中調用父類構造函數。可以使用call() apply()的方法以新創建的對象為上下文執行函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function SuperType(name) { this .colors = [ "red" , "blue" , "green" ]; this .name = name; } function SubType(name) { SuperType.call( this ,name); } let instance1 = new SuperType( '小明' ) let instance2 = new SuperType( '小白' ) instance1.colors .push( 'yellow' ) console.log(instance1) //{name:"小明",colors:["red","blue","green","yellow"]...} console.log(instance2) //{name:"小白",colors:["red","blue","green"]...} //可以傳遞參數 也修復了引用的問題 可以繼承多個構造函數屬性(call多個) |
缺點:
1.只能在構造函數中調用方法 函數不能重用 就是每次子類生成實例的時候都會生成一次屬性和方法
2. 子類無法訪問到父類原型上的方法
組合繼承
綜合了原型鏈和構造函數,將兩者的優點集中了起來。基本的思路是使用原型鏈繼承原型上的屬性和方法,而通過構造函數繼承實例屬性。這樣既可以把方法定義在原型上以實現重用,又可以讓每個實例都有自己的屬性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
function SuperType(name){ this .name = name; this .colors = [ "red" , "blue" , "green" ]; } SuperType.prototype.sayName = function () { console.log( this .name); }; function SubType(name, age){ // 繼承屬性 第二次調用 SuperType.call( this , name); this .age = age; } // 繼承方法 第一次調用 SubType.prototype = new SuperType(); SubType.prototype.sayAge = function () { console.log( this .age); }; let instance1 = new SubType( "Nicholas" , 29); instance1.colors.push( "black" ); console.log(instance1.colors); //["red,blue,green,black"] instance1.sayName(); // "Nicholas"; instance1.sayAge(); // 29 let instance2 = new SubType( "Greg" , 27); console.log(instance2.colors); // ["red,blue,green"] instance2.sayName(); // "Greg"; instance2.sayAge(); // 27 //可以繼承父類原型上的屬性,可以傳參,可復用。 每個新實例引入的構造函數屬性是私有的 |
缺點
調用了兩次父類構造函數 比較耗內存
原型式繼承
即使不自定義類型也可以通過原型實現對象之間的信息共享。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function object(person) { function F() {} F.prototype = person return new F() } let person = { name: '小明' , colors:[ 'red' , 'blue' ] } let person1 = object(person) person1.colors.push( 'green' ) let person2 = object(person) person1.colors.push( 'yellow' ) console.log(person) //['red','blue','green','yellow'] |
適用環境: 你有一個對象,想在它的基礎上再創建一個新對象。你需要把這個對象先傳給object() ,然后再對返回的對象進行適當修改。類似于 Object.create()只傳第一個參數的時候,本質上就是對傳入的對象進行了一次淺復制,缺點就是新實例的屬性都是后面添加的,無法復用
寄生式繼承
與原型式繼承比較接近的一種繼承方式是寄生式繼承,類似于寄生構造函數和工廠模式:創建一個實現繼承的函數,以某種方式增強對象,然后返回這個對象。
1
2
3
4
5
6
7
8
9
10
11
12
|
function object(person) { function F() {} F.prototype = person return new F() } function createAnother(original){ let clone = object(original); // 通過調用函數創建一個新對象 clone.sayHi = function () { // 以某種方式增強這個對象 console.log( "hi" ); }; return clone; // 返回這個對象 } |
寄生式繼承同樣適合主要關注對象,而不在乎類型和構造函數的場景。
缺點:通過寄生式繼承給對象添加函數會導致函數難以重用,與構造函數模式類似
寄生式組合繼承
最常用的繼承方式,也是最佳的,組合繼承會調用兩次父類構造函數,存在效率問題。其實本質上子類原型最終是要包含父類對象的所有實例屬性,子類構造函數只要在執行時重寫自己的原型就行了。基本思路是不通過調用父類構造函數給子類原型賦值,而是取得父類原型的一個副本。說到底就是使用寄生式繼承來繼承父類原型,然后將返回的新對象賦值給子類原型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
//核心代碼 function object(person) { function F(params) {} F.prototype = person return new F() } function inheritPrototype(SubType,SuperType) { let prototype = object(SuperType.prototype) //生成一個父類原型的副本 //重寫這個實例的constructor prototype.constructor = SubType //將這個對象副本賦值給 子類的原型 SubType.prototype = prototype } function SuperType(name) { this .name = name; this .colors = [ "red" , "blue" , "green" ]; } SuperType.prototype.sayName = function () { console.log( this .name); }; function SubType(name, age) { SuperType.call( this , name); this .age = age; } //調用inheritPrototype函數給子類原型賦值,修復了組合繼承的問題 inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function () { console.log( this .age); }; |
總結
到此這篇關于js繼承的6種方式的文章就介紹到這了,更多相關js繼承方式內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/qq_42736311/article/details/115449230