|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function SuperType(){
this.colors=["red","blue","green"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
} 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"
|
将SuperType构造函数放到SubType的执行环境中,这样一来,就会在新SubType对象上执行SuperType()函数中定义的所有对象初始化代码,结果,SubType的每一个实例都有一份具有自己colors属性的副本。
(1)传递参数
借用构造函数可以在子类型构造函数中向父类型构造函数传递参数。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function SuperType(name){
this.name=name;
}
function SubType(){
//继承了SuperType,同时传递参数
SuperType.call(this,"zxj");
//实例属性
this.age=29;
}
var instance=new SubType();
alert(instance.name); //zxj
alert(instance.age); //29
|
(2)借用构造函数的问题
如果仅仅使用借用构造函数,那么将无法避免构造函数模式存在的问题——方法都在构造函数中定义,函数的复用也无从谈起。所以借用构造函数也很少单独使用。
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
29
30
31
32
|
function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
alert(this.name);
}
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age=age;
} SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType; SubType.prototype.sayAge=function(){
alert(this.age);
}
var instance=new SubType("zxj",29);
instance.colors.push("black");
alert(instance.colors); //"red","blue","green","black"
instance.sayName(); //zxj
instance.sayAge(); //29
var instance2=new SubType("Greg",28);
alert(instance.colors); //"red","blue","green","black"
instance2.sayName(); //Greg
instance2.sayAge(); //28
|
组合继承避免了原型链和借用构造函数的缺陷,融合了他们的有点,成为JavaScript中追尾常用的继承模式。而且,instanceof和isPrototypeof()也能够用于识别组合继承创建的对象。
3.原型式继承
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
|
1
2
3
4
5
|
function object(o){
function F(){};
F.prototype=o;
return new F();
} |
在object()函数内部,先创建一个临时性的构造函数,然后将传入的对象作为该构造函数的原型,最后返回这个临时类型的一个新实例。本质上讲,object()对传入其中的对象进行了一次浅复制。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function object(o){
function F(){};
F.prototype=o;
return new F();
} var person={
name:"zxj",
friends:["Shelby","Court","Van"]
}; var anotherPerson=object(person);
antherPerson.name="Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson=object(person);
yetAnotherPerson.name="Linda";
yetAnotherPerson.friends.push("Brabie");
alert(person.friends); //"Shelby,Court,Van,Rob,Brabie"
|
但包含引用类型值的属性始终后悔共享相应的值,这和原型模式一样的。
4.寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式类增强对象,最后再像真的是它做了所有工作一眼放回对象。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function creteAnother(original){
var clone=object(original); //通过调用函数创建一个新对象
clone.sayHi=function(){ //以某个方式来增强这个对象
alert("hi");
}
return clone; //返回这个对象
} var person={
name:"zxj",
friends:["Shelby","Court","Van"]
}; var anotherPerson=createAnother(person);
anotherPerson.sayHi(); //"hi"
|
使用寄生式来为对象添加函数,会由于不能做到函数服用而降低效率。
5.寄生组合式继承
组合继承是JavaScript中最常用的继承模式,但它无论在什么情况下,都会调用两次父类型构造函数:一次在创建子类型原型时,一种在子类型构造函数内部。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
function SuperType(name){ this.name=name;
this.colors=["red","blue","green"];
}
function SubType(name,age){ //继承属性
SuperType.call(this,name); //第二次调用父类型
this.age=age;
} SubType.prototype=new SuperType(); //第一次调用父类型
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
alert(this.age);
}
|
第一次调用父类型时,SubType.prototype会得到两个属性:name和colors;他们都是SuperType的实例属性,只不过位于SubType的原型中。带调用SubType构造函数时,有一次调用了SuperType构造函数,这一次是在新对象上创建实例属性name和colors,于是这两个属性就屏蔽掉了原型中的两个同名属性,调用过程如图所示。
这样就有两组name和colors属性:一组在实例上,一组在SubType的原型中,这就是调用两次SuperType构造函数的结果。
当然,我们可以通过寄生组合是继承来解决这问题,所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链来继承方法。思路:不必为了指定子类型的原型而调用父类型的构造函数,我们只需父类型原型的一个副本。本质上,就是使用寄生式继承来继承父类型的原型,然后将结果指定给子类型的原型,寄生组合式继承的基本模式如下:
|
1
2
3
4
5
|
function inheritPrototype(subType,superType){
var prototype=object(superType.prototype); //创建对象
prototype.constructor=subTyoe; //增强对象
subType.prototype=prototype; //指定对象
} |
示例中inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和父类型构造函数。在函数内部:第一步是创建父类型原型的一个副本。第二步是为创建的副本调价constructor属性,从而弥补因重写原型而失去默认的constrcutor属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我就可以调用inheritPrototype()函数的语句,去替换前面例子中为子类型原型赋值的语句。
|
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
35
36
37
38
39
40
41
42
43
44
|
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //create object
prototype.constructor = subType; //augment object
subType.prototype = prototype; //assign object
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
|