📜 ⬆️ ⬇️

Classes and Metaclasses in Javascript

I want to talk about the solution that I use for single inheritance in JavaScript. It is so small that it is certain that it is found somewhere else somewhere. I hope it will be useful to some readers.

This is part of the framework I made for my Akshell platform, here is the complete solution code and documentation . However, it can be useful in any Server-Side JavaScript environment; on the server side, convenient creation of class hierarchies is especially important. It can also be used on the client if you replace the work with the __proto__ property __proto__ the new operator.

Actually, the whole solution lies in the subclass method of the Function class. If the environment allows, it is worth making it non enumerable.
')
Function.prototype.subclass = function ( /* [constructor] [, prototype] */ ) {
var self = this ;
var constructor = (
typeof (arguments[0]) == 'function'
? Array.prototype.shift.call(arguments)
: ( this === Object
? function () {}
: function () { self.apply( this , arguments); }));
if (arguments[0])
constructor.prototype = arguments[0];
constructor.prototype.__proto__ = this .prototype;
constructor.__proto__ = this .__proto__;
return constructor;
};

The function takes two optional arguments: the constructor and the prototype of the class being created. The semantics of passing arguments is non-standard, but in this case convenient: if the first argument is a function, it is a constructor, otherwise it is a prototype.

Toy class hierarchy example:

var Figure = Object.subclass(
{
getArea: function () {
throw Error( 'Abstract' );
}
});

var Rectangle = Figure.subclass(
function (a, b) {
this .a = a;
this .b = b;
},
{
getArea: function () {
return this .a * this .b;
}
});

var Square = Rectangle.subclass(
function (a) {
Rectangle.call( this , a, a);
});

Because Since constructors are objects of the Function class, then naturally subclasses of Function are classes of classes, or metaclasses. By defining them, you can control the instantiation of classes, as well as determine the static properties and methods of classes that are inherited by subclasses (the subclass receives the metaclass from its parent in the penultimate line of the subclass method).

For example, we want Figure subclasses to be instantiated without an operator and each of them has the optionalNew property equal to true . For this you need to write a metaclass for Figure:

var FigureMeta = Function.subclass(
{
subclass: function () {
var constructor = Function.prototype.subclass.apply( this , arguments);
var result = function () {
var self = (
this instanceof arguments.callee
? this
: {__proto__: arguments.callee.prototype});
constructor.apply(self, arguments);
return self;
};
result.prototype = constructor.prototype;
result.__proto__ = this .__proto__;
return result;
},

optionalNew: true
});

Figure.__proto__ = FigureMeta.prototype;


All Figure subclasses defined after installing the metaclass will receive the necessary changes.

Of course, metaclasses should not be applied too widely, in particular, FigureMeta is a completely contrived example. However, in some cases they can make the code much shorter and clearer. For example, when adapting the js-forms library in Akshell, the Django forms port in JavaScript, metaclasses allowed to create subclasses of forms as declaratively as in Django (there are also metaclasses used for this, only the Python ones). The code takes only 30 lines .

Source: https://habr.com/ru/post/94070/


All Articles