在Javascript(es6)中实现OOP语言抽象方法行为的最佳方法是什么?

在Javascript(es6)中实现OOP语言抽象方法行为的最佳方法是什么?,javascript,oop,inheritance,Javascript,Oop,Inheritance,我是在Java背景下用Javascript(es6)编程的。在Java中,我通常编写接口/抽象类,而es6中缺少此功能。通常,我喜欢编写抽象方法以使用设计模式,例如策略模式。我见过其他人尝试实现他们自己版本的抽象类,但我并不特别喜欢它 假设我们有一个名为Animal的抽象类,它有一个抽象方法cry。据我所见,Javascript代码如下所示: class Animal { constructor() { if (new.target === Animal) { throw ne

我是在Java背景下用Javascript(es6)编程的。在Java中,我通常编写接口/抽象类,而es6中缺少此功能。通常,我喜欢编写抽象方法以使用设计模式,例如策略模式。我见过其他人尝试实现他们自己版本的抽象类,但我并不特别喜欢它

假设我们有一个名为
Animal
的抽象类,它有一个抽象方法
cry
。据我所见,Javascript代码如下所示:

class Animal {
  constructor() {
   if (new.target === Animal) {
    throw new TypeError("Cannot construct Animal instances directly");
   }
  }

  cry() {
    throw new Error('You have to implement the method cry!');
  }
}
让我们假设
Cat
Dog
Animal
的子类。然后,它们的实现如下所示:

class Cat extends Animal {
  cry() {
    return 'meow';
  }
}

class Dog extends Animal {
  cry() {
    return 'woof';
  }
}
class Animal {
  constructor(cryCallback) {
    this.cryCallback = cryCallback;
  }

  cry() {
    return this.cryCallback();
  }
}
function createCat() {
  return new Animal(() => 'meow');
}

function createDog() {
  return new Animal(() => 'woof');
}
console.log(createCat().cry()); //meow
我不喜欢这段代码的地方在于,您必须编写异常语句来指示类是抽象类

然而,我认为有一种更好的方法使用回调和工厂方法。我们可以将回调函数传递到抽象类中,并将其用作假定的抽象方法,而不是为抽象类提供抽象方法。
Animal
示例可以更改为以下内容:

class Cat extends Animal {
  cry() {
    return 'meow';
  }
}

class Dog extends Animal {
  cry() {
    return 'woof';
  }
}
class Animal {
  constructor(cryCallback) {
    this.cryCallback = cryCallback;
  }

  cry() {
    return this.cryCallback();
  }
}
function createCat() {
  return new Animal(() => 'meow');
}

function createDog() {
  return new Animal(() => 'woof');
}
console.log(createCat().cry()); //meow
为了创建
Cat
Dog
对象的实例,基本上可以使用工厂方法创建它们。它将如下所示:

class Cat extends Animal {
  cry() {
    return 'meow';
  }
}

class Dog extends Animal {
  cry() {
    return 'woof';
  }
}
class Animal {
  constructor(cryCallback) {
    this.cryCallback = cryCallback;
  }

  cry() {
    return this.cryCallback();
  }
}
function createCat() {
  return new Animal(() => 'meow');
}

function createDog() {
  return new Animal(() => 'woof');
}
console.log(createCat().cry()); //meow
如果创建一个
Cat
实例并调用
cry
,它将如下所示:

class Cat extends Animal {
  cry() {
    return 'meow';
  }
}

class Dog extends Animal {
  cry() {
    return 'woof';
  }
}
class Animal {
  constructor(cryCallback) {
    this.cryCallback = cryCallback;
  }

  cry() {
    return this.cryCallback();
  }
}
function createCat() {
  return new Animal(() => 'meow');
}

function createDog() {
  return new Animal(() => 'woof');
}
console.log(createCat().cry()); //meow
使用这种方法的优点是,您实际上并没有调用
Animal
的实例,因为您被迫实现
cry
。其次,您可以使用工厂方法隐藏创建
Cat
Dog
对象的实现


请让我知道你对这种方法的看法。有没有更好的方法来实现Javascript中的抽象方法?

在我看来,引发异常的第一个选项要好得多

我想到的第一个原因是:

function createCat() {
  return new Animal(() => 'meow');
}

createCat() instanceof Cat // This line is pointless, cat does not even exist.
但如果您使用:

class Cat extends Animal {
  cry() {
    return 'meow';
  }
}

new Cat() instanceof Cat // true
new Cat() instanceof Animal // true
你有一个使用前者所没有的特性

编辑:您可以通过这样做混合使用这两种代码(我没有检查代码是否正确):


使用typecript,它为您所指的内容提供语法上的糖分。Typescript在编译时还提供可选的类型检查。实际上,我不太喜欢使用
instanceof
。基本上,它违反了Liskov的原则,即:“如果程序模块使用基类,那么对基类的引用可以替换为派生类,而不会影响程序模块的功能”。通常,如果您使用的是
instanceof
,这意味着抽象类的两个子类不能替代抽象类。这对于纯OOP语言是正确的。然而,
instanceof
在JavaScript中仍然非常有用,JavaScript通常用作函数式JavaScript。