构造函数的继承

什么是继承

简单来说,就是一个对象使用另一个对象的属性和方法,并且不要影响原对象的功能。

构造函数的继承

属性继承

一般属性继承是通过call或apply方法,将父对象的构造函数绑定在子对象上

有一个父对象:

1
2
3
4
function Father(hobby, lastName){
this.hobby = hobby
this.lastName = lastName
}

有一个子对象需继承父对象的属性:

1
2
3
4
5
6
7
function Son(hobby, lastName){
Father.call(this,hobby,lastName)
}


var son = new Son('reading','wang');
// {hobby: "reading", lastName: "wang"}

方法继承

方法的继承是通过原型(prototype)来完成

直接继承prototype

还是上面的例子,我们在Father的原型上添加一个showHobby的方法:

1
2
3
4
5
6
7
8
function Father(hobby, lastName){
this.hobby = hobby
this.lastName = lastName
}
//添加
Father.prototype.showHobby = function(){
alert(this.hobby);
}

现在子对象需要继承这个方法,可以将子对象的原型指向父对象,即:

1
2
3
4
5
function Son(hobby, lastName){
Father.call(this,hobby,lastName)
}
//添加
Son.prototype = Father.prototype;

此时Son的实例上就有showHobby方法了:

1
2
3
4
var son = new Son('reading','wang');

son.showHobby();
//reading

现在虽然实现了方法的继承,但是上面这个方法还是有问题的:

第一个问题:constructor的指向问题

constructor是对象的一个属性,指向它的构造函数,每个prototype对象上都有这个属性

按理说,Son的实例,即son,它的构造函数就是Son,然而在这里,它指向了Father:

1
son.constructor == Father

出现这样的原因也很明了,因为Son的原型对象指向了Father的原型对象,Fatherprototype上的constructor显然是Father

这结果会导致继承链的紊乱,son明明是Son构造的。所以我们需要手动修改constructor指向,即添加:

1
Son.prototype.constructor = Son;

第二个问题:任何对Son.prototype的修改都反应到了Father.prototype上

我们在Son的原型上添加一个name属性:

1
Son.prototype.name = 'John';

查看Father原型的变化:

1
Father.prototype

现在,Father的原型上也有了name属性,且值也为john,而且第一个问题中的在Son原型上修改constructor也会反应到Father上,现在Father实例上的constructor也指向了Son

这下更乱套了,所以为了同时解决这两个问题,有个改进的方法。

利用空对象做中介

整个例子完整代码:

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
function Father(hobby, lastName){
this.hobby = hobby
this.lastName = lastName
}
Father.prototype.showHobby = function(){
alert(this.hobby);
}

//创建 Father的实例
var father = new Father('eating','wang');


function Son(hobby, lastName){
Father.call(this,hobby,lastName)
}

//创建中介空对象,并将它的原型指向 Father的原型
var F = function(){}
F.prototype = Father.prototype;
//Son的原型指向为 F的实例
Son.prototype = new F();
//修改 Son的 constructor指向
Son.prototype.constructor = Son;


//Son实例
var son = new Son('reading','wang');


//验证
console.log(son.constructor); //Son

//Son原型添加 name属性
Son.prototype.name = 'John';
console.log(son.name); //jhon
console.log(father.name); //undefined

关键部分就是:

1
2
3
4
5
6
7
//创建中介空对象,并将它的原型指向 Father的原型
var F = function(){}
F.prototype = Father.prototype;
//Son的原型指向为 F的实例
Son.prototype = new F();
//修改 Son的 constructor指向
Son.prototype.constructor = Son;

这样就可以无错地实现了构造函数的继承。