web-dev-qa-db-fra.com

Comment créer une classe de base abstraite en JavaScript qui ne peut pas être instanciée

J'ai un cours

function Node() {
    //implementation
}

et une autre classe

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

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

Comment créer la classe Node () pour qu'elle ne puisse pas être instanciée? par exemple quand j'essaye

var node = new Node();

Donc, cela lève une exception?

33

Cela fonctionnerait:

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();

Remarque: vous ne pouvez pas vous référer à this.prototype à l'intérieur du constructeur, car le prototype n'est qu'une propriété de la fonction constructeur, pas des instances.

Aussi, voir ici pour un bon article sur la façon d'étendre correctement les classes JS.

13
levi

Dans les moteurs JavaScript qui prennent en charge la syntaxe de classe ECMAScript 2015 (aka ES6), cela peut être accompli en utilisant le new.target méta-propriété:

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

ou en utilisant la syntaxe de classe:

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

dans les deux cas, définissez simplement AttributionalNode comme:

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

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

Pour une explication plus détaillée de new.target voir la section 4.2 de ce document .

46

En adaptant la réponse de @ levi, vous pouvez opter pour une solution similaire à utiliser avec ES6 aujourd'hui (comme new.target n'est pas encore établi):

Vous pouvez le voir fonctionner sur la réplique de Babel: 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();
6
Ciro Costa

Sur la base de ces commentaires, j'ai écrit ceci

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(", "));
        }
    }
    }

Usage:

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
0
somla