【问题标题】:How to create Abstract base class in JavaScript that can't be Instantiated如何在 JavaScript 中创建无法实例化的抽象基类
【发布时间】:2015-08-14 01:40:55
【问题描述】:

我有课

function Node() {
    //implementation
}

和另一个班级

function AttributionalNode() {
    this.prototype.setAttr = function (attr) {
        this.atText = attr;
    };
}

AttributionalNode.prototype = new Node();
AttributionalNode.prototype.constructor = AttributionalNode;

如何使类 Node() 不能被实例化? 例如,当我尝试时

var node = new Node();

所以它抛出一个异常?

【问题讨论】:

  • 把throw new Exception() 放在构造函数中,也许吧?我不知道这在 Javascript 中是否有效。
  • 一般来说,在构造函数中设置原型属性并不是一个好主意。

标签: javascript inheritance design-patterns


【解决方案1】:

在支持 ECMAScript 2015(又名 ES6)类语法的 JavaScript 引擎中,这可以使用 new.target 元属性来完成:

function Node() {
   if (new.target === Node) throw TypeError("new of abstract class Node");
}

或使用类语法:

class Node {
   constructor () {
      if (new.target === Node) throw TypeError("new of abstract class Node");
   }
}

在任何一种情况下,只需将AttributionalNode 定义为:

class AttributionalNode extends Node {
   constructor () {
      super();
   }
   setAttr(attr) {
      this.atText = attr;
   }
}

new Node();               // will throw TypeError
new AttributionalNode();  // works fine

有关new.target 的更详细说明,请参阅this document 的第4.2 节。

【讨论】:

【解决方案2】:

这可行:

function Node() {
    if (this.constructor === Node) {
        throw new Error("Cannot instantiate this class");
    }
}

function AttributionalNode() {
    Node.call(this); // call super
}

AttributionalNode.prototype = Object.create(Node.prototype);
AttributionalNode.prototype.setAttr = function (attr) {
    this.atText = attr;
};
AttributionalNode.prototype.constructor = AttributionalNode;

var attrNode = new AttributionalNode();
console.log(attrNode);
new Node();

注意:你不能在构造函数中引用this.prototype,因为原型只是构造函数的属性,而不是实例的属性。

另外,see here 有一篇关于如何正确扩展 JS 类的好文章。

【讨论】:

  • 某个类尝试从Node()继承时报错
  • @AlexandrSargsyan 它没有。我更新了示例来演示这一点。
【解决方案3】:

调整@levi 的答案,您可以使用类似的解决方案来与今天的 ES6 一起使用(因为 new.target 尚未建立):

你可以看到它在 Babel 的 repl 上运行:http://bit.ly/1cxYGOP

class Node {
    constructor () {
      if (this.constructor === Node) 
          throw new Error("Cannot instantiate Base Class");
    }

    callMeBaby () {
      console.log("Hello Baby!");
    }
}

class AttributionalNode extends Node {
  constructor () {
    super();
    console.log("AttributionalNode instantiated!");
  }
}

let attrNode = new AttributionalNode();
attrNode.callMeBaby();

let node = new Node();

【讨论】:

    【解决方案4】:

    虽然问题有一个javascript标签,但因为现在很多项目都在JS之上使用typescript,值得注意的是TS有support for abstract classes and methods out of盒子

    abstract class Animal {
        abstract makeSound(): void;
        move(): void {
            console.log("roaming the earth...");
        }
    }
    

    【讨论】:

      【解决方案5】:

      根据这些cmets,我写了这篇

      class AbstractClass {
          constructor() {
              if(new.target === AbstractClass || this.__proto__.__proto__.constructor === AbstractClass)
                  throw new TypeError("Cannot construct "+ this.constructor.name + " class instances directly");
              let exceptions = {};
              let currProto = this;
              while(currProto.constructor !== AbstractClass ) {
                  for(let method of (currProto.constructor.abstractMethods || [])) {
                      if("function" !== typeof(this[method]))
                          exceptions[method] = currProto.constructor.name;
                  }
                  currProto = currProto.__proto__;
              }
              if(0 !== Object.keys(exceptions).length) {
                  let exceptionsArray = [];
                  for(let method in exceptions) {
                      exceptionsArray.push( exceptions[method] + "." + method);
                  }
                  exceptionsArray.sort();
                  throw new TypeError("Must override the following methods: " + exceptionsArray.join(", "));
              }
          }
          }
      

      用法:

      class MyAbstractClass1 extends AbstractClass {
          static abstractMethods = [
              "myMethod1", // (x:string, y:string): string
              "myMethod2" // (y:string, z:string): string 
          ]
      }
      
      class MyAbstractClass2 extends MyAbstractClass1 {
          static abstractMethods = [
              "myMethod3", // (x:string, y:string): string
              "myMethod4" // (y:string, z:string): string 
          ]
      }
      
      class MyClass extends MyAbstractClass2 {
          myMethod1(x, y){return "apple"}
      }
      
      new MyClass()
      //Error
      

      【讨论】:

        猜你喜欢
        • 2019-04-07
        • 1970-01-01
        • 2010-10-10
        • 2015-08-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多