什么是继承
简单来说,就是一个对象使用另一个对象的属性和方法,并且不要影响原对象的功能。
构造函数的继承
属性继承
一般属性继承是通过call或apply方法,将父对象的构造函数绑定在子对象上
有一个父对象:1
2
3
4function Father(hobby, lastName){
this.hobby = hobby
this.lastName = lastName
}
有一个子对象需继承父对象的属性:1
2
3
4
5
6
7function 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
8function Father(hobby, lastName){
this.hobby = hobby
this.lastName = lastName
}
//添加
Father.prototype.showHobby = function(){
alert(this.hobby);
}
现在子对象需要继承这个方法,可以将子对象的原型指向父对象,即:1
2
3
4
5function Son(hobby, lastName){
Father.call(this,hobby,lastName)
}
//添加
Son.prototype = Father.prototype;
此时Son
的实例上就有showHobby
方法了:1
2
3
4var son = new Son('reading','wang');
son.showHobby();
//reading
现在虽然实现了方法的继承,但是上面这个方法还是有问题的:
第一个问题:constructor的指向问题
constructor是对象的一个属性,指向它的构造函数,每个prototype对象上都有这个属性
按理说,Son
的实例,即son
,它的构造函数就是Son
,然而在这里,它指向了Father:1
son.constructor == Father
出现这样的原因也很明了,因为Son
的原型对象指向了Father
的原型对象,Father
的prototype
上的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
36function 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;
这样就可以无错地实现了构造函数的继承。