Is it possible to create an' abstract class ' in Javascript?

When creating a class in Javascript whose attributes and methods are all static (for example, for storing predefined settings for a game), I would like to know if it is possible to set the class to abstract in a similar way to what can be realized in other languages (where the clause 'abstract' is available or where one can simply set the constructor to protected).

In other words: whereas this example available in JSFiddle , is it possible to prevent building instances of the 'StaticTest' class (on line 36)? If the constructor (line 7) is not defined, the code does not execute (throwing the exception "Uncaught ReferenceError: StaticTest is not defined").

// INICIO - Apenas para evitar poluir o espaço global
(function(){

    /**
     * Construtor da classe (é realmente necessário?).
     */
    var AbstractTest = function() {
    };

    /**
     * Atributo com o texto de teste.
     */
    AbstractTest.Attribute = "O atributo diz: Olá Planeta!";

    /**
     * Método que retorna o texto do teste.
     * @return String com o texto de teste.
     */
    AbstractTest.Method = function() {
        return "O método diz: Olá Mundo!";
    }

    // Atribui a classe ao escopo global 'window'
    window.AbstractTest = AbstractTest;

// FIM - Apenas para evitar poluir o espaço global
}());

/**
 * Função de teste de instanciação.
 * @return String com o texto de teste de uma instância criada.
 */
window.testInstantiation = function() {

    // A dúvida é sobre a possibilidade de impedir a execução dessa linha:
    var oTest = new AbstractTest();

    oTest.Attribute = "O atributo na nova instância diz: Olá Brasil!";
    return oTest.Attribute + " e " + AbstractTest.Attribute;
}

Note: the question is just a curiosity about language characteristics; not that the possibility of class instantiation is necessarily a problem.

EDIT: changed to fix and use the term " abstract "instead of"static".

Author: Luiz Vieira, 2014-01-14

4 answers

In JavaScript there are no static classes, properties, or methods. The closest to this are properties and methods assigned directly to a constructor (as you did), or a simple literal object.

If you use a literal object to define StaticTest, then new StaticTest() will generate a TypeError. It's a way to solve the problem (with try..catch around the line that throws the exception).

Another way is to change the return of the constructor. I did not understand exactly what you want in the test, but it is possible to return the object itself StaticTest, or any other object:

var StaticTest = function() {
    // return StaticTest;
    // ou
    // return {};
};

But beware: if you try to return a value that is not an object (such as false), the constructor will return this, i.e. the newly created instance.


Considering your edit that swaps "static" for "abstract", I think the best way out would even be to use a literal object, since the intention is to avoid instantiation:

var StaticTest = {
    attribute: "O atributo diz: Olá Planeta!",
    method: function(){}
    // etc.
};

Thus, StaticTest would throw an exception, that you can capture with try..catch:

try {
    new StaticTest();
} catch(e) {
    // trate a exceção aqui
}
 6
Author: bfavaretto, 2014-01-14 13:55:00

You can create a immutable object in JavaScript using the Object.freeze, and an immutable reference through the method Object.defineProperty:

// Atribui a classe ao escopo global 'window'
Object.freeze(StaticTest);
Object.defineProperty(window, "StaticTest", { 
    value:StaticTest,
    configurable:false,
    writable:false
});

// Todas essas tentativas vão falhar silenciosamente:
// (ou lançar uma exceção, se o modo "strict" estiver ativado)
StaticTest = outroObjeto;
window.StaticTest = outroObjeto;
StaticTest.StaticAttribute = outroValor;
delete StaticTest.StaticAttribute;

As for preventing an object from being inherited, I know of no way to do this, and I believe it is not possible at all.

Oo classical vs. prototypical

Notice that you asked about classes , but I gave my answer talking only about objects. Why? Simply because, strictly speaking, JavaScript does not have the concept of "classes".

In classical object orientation, used by the vast majority of languages that follow this paradigm, classes and objects (or instances) are distinct concepts: the class defines the "structure" and "behavior" of its objects, and each object belongs to a single class. Class inherits from class, so that instances of the specific class have structure and behavior similar to instances of the general class.

In prototypical OO, there are only objects. An object defines its own structure and behavior, independently of the others. To reuse these characteristics in other objects, they inherit directly from the existing object (here called prototype), modifying whatever you want and keeping (sharing) the rest. There are no classes, only functions constructors .

JavaScript inheritance

For historical reasons, although conceptually JavaScript is a language that follows the prototypical OO, its syntax tries to "hide" the fact - making it more somewhat like the classic one. The result is a "salad", as I will illustrate below:

// Objeto simples: sem classe, sem construtor
var obj = {
    atributo:"planeta",
    metodo:function() {
        return "Olá, " + this.atributo + "!";
    }
}

// Construindo um objeto que herda de "obj"
function herdarDeObj() { }
herdarDeObj.prototype = obj;

var obj2 = new herdarDeObj();
alert(obj2.metodo()); // "Olá, planeta!"
obj2.atributo = "mundo";
alert(obj2.metodo()); // "Olá, mundo!"

alert( obj.isPrototypeOf(obj2) ); // true
alert( obj === Object.getPrototypeOf(obj2) ); // true

// Sintaxe confusa
alert( obj === obj2.prototype ); // false
alert( obj2.prototype ); // undefined

alert( obj === herdarDeObj.prototype ); // true
alert( obj === Object.getPrototypeOf(herdarDeObj) ); // false
alert( Object.getPrototypeOf(herdarDeObj) ); // function Empty() {}
                                             // (varia conforme o browser)

alert( obj2 instanceof obj ); // false
alert( obj2 instanceof herdarDeObj ); // true
herdarDeObj.foo = "bar";
alert( obj2.foo ); // undefined
obj.foo = "baz";
alert( obj2.foo ); // "baz"

As you can see, we have two objects obj and obj2 in which the second inherits from the first (or: the first is prototype of the second). Meanwhile, JavaScript it "hides" this simple relationship, forcing us to create a constructor method, assign it the property prototype and invoke it via the keyword new.

But note that obj it is not prototype of herdarDeObj - it is prototype of the objects constructed through the command new herdarDeObj(). The constructor is a normal function, so much so that its prototype is the "empty function".

Probably due to this fact (the constructor define everything regarding the object-both the attributes initials, placed in the body of the constructor through this.atr = val, as for the prototype, the one from whom the object will inherit) people confuse it with the "class" of the object. And for the convenience that this constructor method offers, rarely does anyone [explicitly] use prototypical inheritance in practice, so much so that there are plans to introduce the concepts of classical OO into future versions of JavaScript. Maybe someday, that's what you ask is in fact possible.

Conclusion

Since there are no classes in JavaScript, it makes no sense to talk about "static classes" (not even "static attributes" or "class attributes"). If you want to expose a collection of attributes and methods by means of a given name, the most natural thing is to do this using a simple object:

window.StaticTest = {
    StaticAttribute:"O atributo estático diz: Olá Planeta!",
    StaticMethod:function() {
        return "O método estático diz: Olá Mundo!";
    }
};

You can make the object and reference immutable, as I have already explained, but you cannot prevent other objects from inheriting from it: anyone can create a new constructor function, assign StaticTest as its prototype and call that function - producing objects that inherit from StaticTest.

 9
Author: mgibsonbr, 2014-01-14 07:22:22

There is serious confusion here about concepts of classes and members (attributes) in the question.

Static attributes

Are those that, regardless of the instance of the class/function used, return the same value or same object instance.

The best example of this in Javascript is the use of prototype, as we can see in @mgibsonbr's answer.

Immutable, invariant or constant objects

We cannot confuse static with immutable . In Java, for example, an immutable attribute or variable is declared with final and not with static.

In javascript, it seems that the best option is Object.freeze, as we can see in @mgibsonbr's answer.

Visibility

Was cited in the question the question of preventing external instantiation of the class. In languages like Java, this is achieved by creating a private constructor. See, again, that this has nothing to do with the concepts of static or immutable .

Conclusion

To achieve your goal, use a unique setting, my suggestion is to simply use a global variable.

There are some more advanced techniques of how to implement the Singleton project pattern in Javascript (Here, here and Here), but if you have control of the code, just set an access pattern if you follow it throughout development. There is no technique that saves you from not following your own design , so don't spend too much time protecting your code from yourself.

On the other hand, for the creation of libraries, frameworks and APIs the story is different.

 7
Author: utluiz, 2017-05-23 12:37:32

One way to build an abstract "class", with the usual inheritance of prototypes in Javascript, is by using a constructor like the following:

/**
 * Construtor da classe abstrata.
 * @abstract
 * @constructor
 */
var Base = function() {
    if (this.constructor === Base) {
      throw new Error("Não pode instanciar classe abstrata!");
    }

    // .... código do construtor
};

The constructor checks if it was called direct (as in new Base()) with the property constructor, and throws an exception in case this happened.

This "class" can only be used if it is derived:

/**
 * Construtor da classe derivada.
 * @constructor
 */
var Derived = function() {
    Base.apply(this, arguments);
};

Derived.prototype = Object.create(Base.prototype);
Derived.prototype.constructor = Derived;

The derived class calls the constructor of the base class with apply, and creates its prototype based on that of the base class with Object.create. It is also very important to assign the constructor correctly to Derived, otherwise you will not be able to create instances of Derived.

A more extensive example can be found here .

Note the use of JSDoc @abstract.

 3
Author: Jordão, 2014-01-14 19:02:28