JavaScript--淺談繼承

ECMAScript實現繼承的方式:程序員

1.原型鏈ide

利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。函數

function Father(){
   this.name = 'father'  
}

function Son(){
    
}

//繼承Father,將Father的實例賦給Son的原型
Son.prototype = new Father()

var son1 = new Son()
son1.name  // father     繼承了Father的name屬性
View Code

以前在《淺談建立對象》中,咱們提出 原型模式建立對象 是能夠修改原型的,以下this

 1 function Father(){
 2   this.name = 'father'  
 3 }
 4 
 5 function Son(){
 6 }
 7 
 8 //給Son添加原型屬性
 9 Son.prototype.age = '1'
10 
11 //重寫Son原型對象,繼承Father
12 Son.prototype = new Father()
13 
14 //建立Son實例,能夠放在修改原型以前,修改原型可以從實例上當即反映出來
15 var son1 = new Son()
16 
17 Son.prototype.job = '程序員'
18 
19 son1.age // error  
20 //在重寫Son原型對象以前 給Son原型添加屬性是沒用的,重寫會切斷與以前原型對象的聯繫
21 
22 son1.job //  程序員
23 
24 Son.prototype = {
25      height: 1.70  
26 }
27 
28 son1.height  // error  上述方法也是屬於重寫原型對象
View Code

相似於原型模式,單獨使用原型鏈實現繼承的問題:spa

a.原型鏈中的引用類型的值會被全部實例共享,並能夠修改,而修改後會反映到全部的實例上,這樣實例就不能擁有私有的引用類型的屬性。prototype

b.在建立子類型實例時,咱們沒法在不影響其餘實例的同時向父類型的構造函數中傳遞參數。3d

 

2.借用構造函數code

function Father(name){
  this.name = name; 
  this.colors = ['red','green','blue']  
}

function Son(name){
  //在子類型構造函數中調用父類型的構造函數,經過call函數等強制綁定做用域
  Father.call(this,name)  
}

var son1 = new Son('張三')
son1.name  //張三
son1.colors.push('yellow')

var son2 = new Son('李四')
son2.name  //李四

son1.colors  // ['red','green','blue','yellow']
son2.colors // ['red','green','blue']
View Code

相對原型鏈而言,借用構造函數能夠向父類型的構造函數傳參對象

相似於構造函數模式,單獨使用借用構造函數模式實現繼承的問題:blog

a.方法都在構造函數中定義,每次建立對象就會從新實例化一次方法屬性

b.父類型的原型屬性,子類型是訪問不到的。

 

3.組合繼承

使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。

 1 function Father(name){
 2  this.name = name;
 3  this.colors = ['red','green','blue']
 4 }
 5 Father.prototype.sayName = function (){
 6    alert(this.name)
 7 }
 8 
 9 function Son(name,age){
10  Father.call(this,name);
11  this.age = age;
12 }
13 
14 //重寫Son的原型實現繼承,這種重寫會致使Son的實例的constructor屬性都變成父類型的構造函數Father,Son.constructor  //Father 而不是Son
15 Son.prototype = new Father()
16 //對於某些對constructor屬性比較在乎的場景,能夠手動的將Son的constructor屬性設置成Son,Son.constructor  //Son
17 Son.prototype.constructor = Son;
18 Son.prototype.sayAge = function(){
19    alert(this.age)
20 }
21 
22 var son1 = new Son('張三',20)
23 son1.colors.push('yellow')
24 son1.sayName(); //張三
25 son1.sayAge(); //20
26 
27 var son2 = new Son('李四',21)
28 son2.sayName(); //李四
29 son2.sayAge(); //21
30 
31 son1.colors  //  ['red','green','blue','yellow']
32 son2.colors //  ['red','green','blue']
View Code

不管什麼狀況下,都會調用2次父類型的構造函數,

第一次在建立子類型原型的時候,Son.prototype = new Father(),

第二次在子類型構造函數內部,Fahter.call(this,name)

4.原型式繼承

 1 var Father = {
 2     name:'father',
 3     friends:['張三','李四']  
 4 }
 5 //只傳入一個參數的狀況下,這2種寫法的行爲同樣的
 6 var son1 = Object(Father)
 7 son1.name = '張三'
 8 var son2 = Object.create(Father)
 9 son2.friends.push('王五')
10 
11 son1.name // 張三
12 son2.name // 張三  ,使用object.create 相似於原型鏈的繼承方式,超類原型的全部屬性都是共享的
13 son1.friends // ['張三','李四']  
14 son2.friends // ['張三','李四','王五']
15 
16 var son3 = Object.create(Father,{
17    name:{
18         value:'王五'
19    }    
20 })  
21 son3.name // 王五
22 son3.friends // ['張三','李四','王五']
View Code

 

在不想建立構造函數,只是想一個對象與另外一個對象保持相似的狀況下,原型式繼承是能夠勝任的,不過他們的引用類型的屬性仍是全部實例共享的。

5.寄生式繼承

function createSon(Father){
 var Son = Object(Father);
  //在這裏能夠添加屬於子類型的屬性  
 Son.sayHi = function(){
    alert('hi')
 }
 return Son
}
View Code

和原型式繼承同樣,在不考慮自定義類型和構造函數的狀況下,只想返回一個相似的新對象時,寄生式繼承能夠作到,不過和構造函數模式相似,方法都在函數中定義,每次建立對象就會從新實例化一次方法屬性,全部的實例的sayHi方法都是一個新的實例

6.寄生組合式繼承

爲了解決組合繼承調用2次父類型構造函數的問題

首先咱們要搞清楚爲何組合繼承會調用2次父類型的構造函數,組合繼承的2次調用分別是 子構造函數中的 借用構造函數 和 原型鏈繼承的實現。

其實多餘的一次調用就是 原型鏈繼承的實現中咱們必需要拿到一個父類型的實例用來做爲子類型的原型,這樣修改子類型的原型只會修改父類型的實例原型,而不會影響父類型的其餘實例

這就是爲何 在原型鏈繼承中,咱們用 Son.prototype = new Father() //獲取父類型的實例重寫子類型的原型  而不是 Son.prototype = Father.prototype // 這樣寫的話,咱們在Son.prototype上新添加一個屬性,會影響到Father的其餘實例

怎麼才能只得到一個與父類型prototype類似的對象而不調用父類型的構造函數呢?

在不考慮自定義類型和構造函數的狀況下,只想返回一個相似的新對象時,寄生式繼承能夠作到

fucntion expendPrototype(Son,Father){
  //建立父類型的原型副本
  var prototype = Obejct(Father.prototype);
  //彌補重寫原型而失去的默認的constructor屬性
  prototype.constructor = Son;
  //重寫子類型的原型
  Son.prototype = prototype;
}
View Code

這樣 咱們用這個方法去代替以前的 Son.prototype = new Father(),從而解決了多一次無用的構造函數調用.

相關文章
相關標籤/搜索