How to create Abstract base class in JavaScript that can't be Instantiated

11,131

Solution 1

This would work:

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

Note: you cannot refer to this.prototype inside the constructor, as the prototype is only a property of the constructor function, not of the instances.

Also, see here for a good article on how to properly extend JS classes.

Solution 2

In JavaScript engines that support ECMAScript 2015 (aka ES6) class syntax, this can be accomplished using the new.target meta-property:

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

or using class syntax:

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

in either case, just define AttributionalNode as:

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

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

For a more detailed explanation of new.target see section 4.2 of this document.

Solution 3

Adapting @levi's answer, you can go with a similar solution for using with ES6 today (as new.target isn't established yet):

You can see it running on Babel's 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();

Solution 4

Although the question has a javascript tag, because nowadays a lot of projects are using typescript on top of JS, it's worth noting that TS has support for abstract classes and methods out of the box

abstract class Animal {
    abstract makeSound(): void;
    move(): void {
        console.log("roaming the earth...");
    }
}
Share:
11,131
Alexandr Sargsyan
Author by

Alexandr Sargsyan

Updated on June 03, 2022

Comments

  • Alexandr Sargsyan
    Alexandr Sargsyan about 2 years

    I have a class

    function Node() {
        //implementation
    }
    

    and another class

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

    How to make class Node() so it can't be instantiated? e.g when I try

    var node = new Node();
    

    So it throws an Exception?

  • Admin
    Admin about 9 years
    Can you provide a reference for new.target?
  • Alexandr Sargsyan
    Alexandr Sargsyan about 9 years
    It throughs error when some class try to inherit from Node()
  • levi
    levi about 9 years
    @AlexandrSargsyan It does not. I updated the example to demonstrate this.
  • Ciro Costa
    Ciro Costa about 9 years
    If you wish to know whether it supports or not: kangax.github.io/compat-table/es6/#new.target . At this moment, no support, as stated.
  • zypA13510
    zypA13510 almost 6 years
  • Donald Duck
    Donald Duck over 4 years
    Is there any difference between new.target and this.constructor?