利用 JS 模拟抽象类

1,970 阅读2分钟

前言

抽象类有两个非常典型的特点:

  1. 不能实例化
  2. 抽象类的非抽象继承子类必须重写抽象类的所有抽象方法

下面就基于这两个特点,利用 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 方法