如何在 JavaScript 中创建无法实例化的抽象基类

Posted

技术标签:

【中文标题】如何在 JavaScript 中创建无法实例化的抽象基类【英文标题】:How to create Abstract base class in JavaScript that can't be Instantiated 【发布时间】: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 中是否有效。 一般来说,在构造函数中设置原型属性并不是一个好主意。 【参考方案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 节。

【讨论】:

可以提供new.target的参考吗? 如果您想知道它是否支持:kangax.github.io/compat-table/es6/#new.target。如上所述,目前不支持。 MDN: new.target new.targetthis.constructor有区别吗?【参考方案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

【讨论】:

以上是关于如何在 JavaScript 中创建无法实例化的抽象基类的主要内容,如果未能解决你的问题,请参考以下文章

如何从字符串中提取值以在 Scala 中创建案例类实例

如何在python中创建一个在间隔函数调用上线程化的后台?

如何在 Javascript 中创建静态字段 [重复]

无法关闭在 Interface Builder 中创建的 NSView 实例的剪辑

如何在 Parse cloud Javascript SDK 中创建此查询?

C#winform中为啥一个窗体的对象可以调用在另一个窗体中创建的一个类未实例化下