前言
抽象类有两个非常典型的特点:
- 不能实例化
- 抽象类的非抽象继承子类必须重写抽象类的所有抽象方法
下面就基于这两个特点,利用 ES6 的 class 来模拟一个抽象类。
具体实现
思路分析1:针对抽象类的第一个特点,不能实例化
当我们实例化一个 class 时,需要使用 new 操作符,此时 new.target 属性的值会指向当前 class,所以我们可以利用这一特点,来阻止实例化。
代码如下:
class Animal {
constructor(name, age) {
// 抽象类不允许实例化
if (new.target === Animal) {
throw new Error('Animal class 不允许实例化');
}
this.name = name
this.age = age
}
}
new Animal() // Uncaught Error: Animal class 不允许实例化
思路分析2:针对抽象类的第二个特点,抽象类的非抽象继承子类必须重写抽象类的所有抽象方法。
我们可以在实例化子类对象时进行拦截,当对子类进行实例化时,会调用父类的构造函数,此时 new.target 属性的值会指向子类 class,所以我们可以在父类的构造函数中拿到子类的原型对象,这样我们就可以通过遍历父类原型上的方法,判断在子类的原型中是否存在,如果不存在则抛出异常。
代码如下:
class Animal {
constructor(name, age) {
// 抽象类不允许实例化
if (new.target === Animal) {
throw new Error('Animal class 不允许实例化');
}
// 继承抽象类的子类必须覆写抽象类的所有抽象方法
// 此处假设抽象类的所有方法都是抽象的
Reflect.ownKeys(Animal.prototype).forEach(key => {
if (!new.target.prototype.hasOwnProperty(key)) {
throw new Error(`请覆写 ${key} 方法`);
}
})
this.name = name
this.age = age
}
// 抽象方法
getName() {}
}
class Cat extends Animal{
constructor(name, age) {
super(name, age)
}
}
new Cat('豆豆', 3) // VM810:9 Uncaught Error: 请覆写 getName 方法