Javascript之繼承(原型鏈方式)

weixin_34219944發表於2013-05-21
1.原型鏈
原型鏈是JavaScript中繼承的主要方法。
每個建構函式都擁有一個原型物件,原型物件都包含一個指向建構函式的指標(constructor),例項都包含一個指向原型物件的內部指標(__proto__)。
假如,原型物件(Son.prototype)等於另一個型別(Pserson)的例項(person1),那麼此時的原型物件(Son.prototype)將包含一個指向另一個原型(Person.prototype)的指標,相應的,另有一個原型(Person.prototype)中也包含著一個指向另一個建構函式(Person())的指標。
再如,另一個原型(Person.prototype)又是另一個型別(Person)的例項(person1),那麼上述關係依舊成立,如此層層遞進,就構成了例項與原型的鏈條,這就是所謂的原型鏈。
 ​
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
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; 
    
    
var instance=new Son(); 
alert(instance.getSuperValue());       //true 
    
alert(instance instanceof Object);     //true 
alert(instance instanceof SuperType);  //true 
alert(instance instanceof SubType);    //true

以上程式碼定義了兩個型別:SuperType和SubType。每個型別分別有一個屬性和方法。它們主要區別就是SubType繼承了SuperType,而這繼承是通過建立SuperType新例項,並將這個新例項賦給SubType.prototype實現的。實現的本質就是重寫原型物件,代之以新型別的例項。

21215810-0c113df43d1948a28f2a0535fd974e57.png 

instance指向Subtyoe.prototype,SubType.prototype又指向了SuperType.prototype。getSuperValue()方法仍然還在SuperType。prototype中,但property則位於SubType.prototype中。這是因為property是一個例項屬性,而getSyoerValue()則是一個原型方法。既然SubType.prototype現在是SuperType的例項,那麼property當然位於該例項中。此外,要注意instance.constrcutor現在指向的是SuperType,這是因為原來SubType.propertype中的constructor被重寫了的緣故。

注:SubType.propertype中的constructor不被重寫了的緣故,而是SubType的原型指向了另一個物件——SuperType的原型,而這個原型物件的constructor是指向SuperType的。

通過實現原型鏈,本質上就是擴充套件了原型搜尋機制。

2.別忘了預設的原型

事實上,我們的原型鏈還少了一環,所有引用型別預設繼承了Object型別,而這個繼承也是通過原型鏈實現的。我們要記住,所有函式的預設原型都是Object的例項,因此預設原型都會包含一個內部指標,指向Object.prototype。這也正式自定義型別能使用toString()等預設方法的原因。

21215813-8087a7d58c68486d88c263731c994da8.png 

一句話,SubType整合了SuperType,而SuperType繼承了Object。當呼叫instance.toSring()時,實際呼叫的是儲存在Object.prototype中的那個方法。

3.確定原型和例項的關係

可以使用instanceof操作符來測試例項與原型鏈中出點過的建構函式。 

1
2
3
alert(instance instanceof Object);     //true
alert(instance instanceof SuperType);  //true
alert(instance instanceof SubType);    //true

4.謹慎的定義方法

子型別有時需要重寫父型別中的某個方法,或者需要新增父型別中不穿在的某個方法,但不管怎麼,給原型新增的程式碼一定要放在替換原型的語句之後

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
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;
    }
  
//重寫父型別中的方法
SubType.prototype.getSuperValue=function(){
    return this.subproperty;
    }
  
var instance=new SubType();
alert(instance.getSuperValue());         //false

注意,在通過原型鏈實現繼承時,不能使用物件字面量建立原型物件,這樣會重寫原型鏈。

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
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;
        },
    someOtherMethod:function(){
        return false;
        }
          
    }
var instance=new SubType();
alert(instance.getSuperValue());         //error

由於現在的原型包含的是一個Object的例項,而非SuperType的例項,因此我們設想中的原型鏈已經別切斷——SubType和SuperType已經沒關係了。

5.原型鏈的問題

​原型鏈最主要的問題還是來支援引用型別的原型,我們以前說過包含引用型別值的原型屬性會被所有例項共享,這也是為什麼要在建構函式中定義屬性,而不是在原型中定義屬性的原因了。在通過原型來實現繼承時,原型實際上會被變成另一個型別的例項。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function SuperType(){
    this.colors=["red","blue","green"];
    }
      
function SubType(){
}
  
//繼承了SuperType
SubType.prototype=new SuperType();
  
var instance=new SubType();
instance.colors.push("black");   //"red","blue","green","black"
alert(instance.colors);
  
var instance2=new SubType();
alert(instance2.colors);          //"red","blue","green","black"

原型的第二個問題:在建立子類的例項時,不能向父型別的建構函式草地引數,所以實踐中很少單獨用到原型鏈。





相關文章