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實現的。實現的本質就是重寫原型對象,代之以新類型的實例。
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()等默認方法的原因。
一句話,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"
|
原型的第二個問題:在創建子類的實例時,不能向父類型的構造函數草地參數,所以實踐中很少單獨用到原型鏈。