KonishiLee's Blog

继承

背景

JavaScript 面向对象编程 —— 继承,是非常重要的一个知识点,那今天就来讲一下怎样来实现 JS 的继承。

场景

首先创建一个对象 Car 的构造函数

1
2
3
function Car(){
this.species = '汽车';
}

然后再创建一个对象 Taxi 的构造函数

1
2
3
4
5
function Taxi(brand, color){
this.brand = brand;
this.color = color;
}

现在要实现的是让 Taxi 继承 Car,有哪些实现方式呢?

1.构造函数绑定

这种方式非常简单,就是只需要将 Car 构造函数绑定到 Taxi 上就可以了。要实现只需要增加一行代码。

这里区分了 call 和 apply 的使用方法。

1
2
3
4
function Taxi(brand, color){
Taxi.call(this, brand, color);
Taxi.apply(this, arguments);
}

2.Prototype 继承

在 Taxi 的构造函数内,如果它的 prototype 指向 Car,那么 Taxi 就可以实现 Car 下的所有属性了。所以可以简单修改一下代码。

1
2
3
4
5
6
7
8
9
function Taxi(brand, color){
Taxi.prototype = new Car();
this.brand = brand;
this.color = color;
}
var taxi = new Taxi('宝马', '黄色');
console.log(taxi.species); //汽车

但是这里有一个问题,可以输出 taxi 看看,会发现 taxi 的构造函数 Car,但是我们这里明明是用 Taxi 来生成一个 taxi,为什么 taxi.prototype.constructor 指向的是 Car,所以在这里需要处理一下这个错误,重新修改一下代码。

1
2
3
4
5
6
7
8
9
10
function Taxi(brand, color){
Taxi.prototype = new Car();
Taxi.prototype.constructor = Taxi;
this.brand = brand;
this.color = color;
}
var taxi = new Taxi('宝马', '黄色');
console.log(taxi);

这里再输出一下 taxi,看看它的构造函数就是 Taxi 了,看来是正确了。

直接继承 Prototype

1
2
3
function Car(){}
Car.prototype.species = '汽车';
1
2
3
4
5
6
7
8
9
10
11
12
function Taxi(brand, color){
this.brand = brand;
this.color = color;
}
Taxi.prototype = Car.prototype;
Taxi.prototype.constructor = Taxi; // 上面已经解释过了
var taxi = new Taxi('宝马', '黄色');
console.log(taxi.species); //汽车

这样继承是很方便的,省去内存,因为并不需要实例化 new Car() 了,但是这样继承有什么样的缺点呢?这里直接用 Taxi.prototype = Car.prototype,那么在执行 Taxi.prototype.constructor = Taxi; 的时候直接就将 Car.prototype.constructor 给改变了,所以这样的继承一般是不推荐使用的。

利用空对象来实现继承

1
2
3
4
5
6
7
8
9
10
11
12
function Taxi(brand, color){
this.brand = brand;
this.color = color;
}
var F = function(){}
F.prototype = Car.prototype;
Taxi.prototype = new F();
Taxi.prototype.constructor = Taxi;
var taxi = new Taxi('宝马', '黄色');
console.log(taxi.species); //汽车

这样也实现了继承,使用了一个空对象来中一个中介,其实就是改进了一下上面的直接继承 Prototype,因为这样在执行修改 Taxi.prototype.constructor 就不会影响到 Car.prototype.constructor 了。

重新再整理一下代码。

1
2
3
4
5
6
7
8
9
10
11
12
function extend(Child, Parent){
var F = function(){}
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
extend(Taxi, Car);
var taxi = new Taxi('宝马', '黄色');
console.log(taxi.species); //汽车

这里的 extend 方法就是实现了继承,但是为了增加这种继承方法的完整性,需要增加一个属性。

1
Child.uber = Parent.prototype;

上面意思呢,uber 的大意就是向上的意思,这里指代增加属性 uber,可以直接调用父类的方法。(类似于 class 的 super)

拷贝继承

上面其实我们是用了 prototype 的属性进行继承,那如果赋值的形式可以,拷贝也应该可以吧,那我们改一下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function extend(Child, Parent){
var p = Parent.prototype;
var c = Child.prototype;
for(i in p){
c[i] = p[i];
}
c.uber = p;
}
extend(Taxi, Car);
var taxi = new Taxi('宝马', '黄色');
console.log(taxi.species); //汽车
如果喜欢这个分享,就帮忙买杯咖啡吧