11-继承
9.2.6 继承
在分析原型的时候,已经看到了继承的身影:当创建一个类的实例时,它继承了类原型中所有的功能。尽管如此,它并没有就此打住:如果一个方法没有在对象原型中找到其定义,它会检查原型的原型。这样就建立了一个原型链。JavaScript会沿着原型链走下去,直到某个原型满足了需求。如果找不到这样的原型,程序最终会报错。
原型链最大的好处是能够建立类的层次结构。前面已经讨论了汽车如何归属于一种交通工具。原型链允许将功能置于最合适的继承层次上。例如,汽车可能有个方法叫 deployAirbags ,可以将它当做一般的交通工具方法,但是,大家坐过配备安全气囊的船吗?另一方面,几乎所有的交通工具都可以搭载乘客,所以交通工具都可能有 addPassenger 的方法(可以在超载时抛出异常)。下面看看如何在JavaScript中实现这种场景:
class Vehicle {
constructor() {
this.passengers = [];
console.log("Vehicle created");
}
addPassenger(p) {
this.passengers.push(p);
}
}
class Car extends Vehicle {
constructor() {
super();
console.log("Car created");
}
deployAirbags() {
console.log("BWOOSH!");
}
}
上面这段代码中,第一次出现了 extends 关键字,这个语法标志着 Car 是 Vehicle 的子类。其次 super() 也是之前没有见过的。在JavaScript中,super是一个特殊的函数,它调用了父类的构造器。子类必须调用这个方法,否则会报错。
看看下面这个例子:
const v = new Vehicle();
v.addPassenger("Frank");
v.addPassenger("Judy");
v.passengers; // ["Frank", "Judy"]
const c = new Car();
c.addPassenger("Alice");
c.addPassenger("Cameron");
c.passengers; // ["Alice", "Cameron"]
v.deployAirbags(); // 报错
c.deployAirbags(); // "BWOOSH!"
注意,可以在 c 上调用 deployAirbags ,但是不能在 v 上调用。换言之,继承是单向的。 Car 类的实例可以访问所有 Vehicle 类的方法,反之却不行。