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"
|
原型的第二个问题:在创建子类的实例时,不能向父类型的构造函数草地参数,所以实践中很少单独用到原型链。