mathematical.coffee mathematical.coffee - 1 month ago 15
Javascript Question

Making subclass with .prototype and __proto__

I've recently been learning javascript by writing some gnome shell extensions, and hence my understanding of Javascript has been shaped by the examples I've observed in the gnome-shell javascript sources. I have a feeling I've been understanding classes wrong and just want some clarification.

I've written a few of my own subclasses, and in each case I've defined them simply by following similar code in the gnome-shell javascript source:

Subclass = function() {
this._init.apply(this,arguments);
}
Subclass.prototype = {
__proto__: Superclass.prototype,
_init: function() {
Superclass.prototype._init.call(this);
},
// add other methods of Subclass here.
}


Up to now I thought this was the standard way of making a class
Subclass
that was basically
Superclass
plus extras. I assumed that every object had a
_init
method.

I've recently tried to apply the same method to make a subclass of a
Clutter.Actor
(what's important is that it's not a GNOME-shell-defined class), and realised that the above way of subclassing objects is not the standard. For one, not every class has a
_init
function as I assumed; this is just something that GNOME-shell has done in their javascript classes.

So, my questions are:


  1. Is there any documentation regarding the above method of creating subclasses? All the tutorials I've seen say to set
    Subclass.prototype = new Superclass()
    instead of doing the
    Subclass.prototype = { __proto__:Superclass.prototype, define_prototype_methods_here }
    method, but my thought is that there must be some method to it if gnome-shell uses it consistently?

  2. If I wanted to stay as close as possible to the above way of defining classes (just so I can retain some code similarity to GNOME-shell for which I am writing extensions), what do I replace
    Superclass.prototype._init.call(this)
    with
    in
    Subclass._init
    to make sure that the
    Subclass.prototype
    gets all the methods/properties of
    Superclass
    (which I then add on to in my definition of
    Subclass.prototype
    ), if the
    Superclass
    doesn't have an
    _init
    function (i.e. does it have some equivalent constructor function I call)?



I'm really confused about this all so please forgive me if my question doesn't make much sense; it'll be because of the extent of my misunderstanding & confusion!

EDIT: clarification:
- I know the
__proto__
is not recommended because it is non-standard, but my code is never going to run in a browser - it's only ever going to run with GNOME javascript (which is basically the Mozilla javascript engine), so I don't need to worry about cross-compatibility.

Answer

As already said, don't use __proto__. It's a non-standard property. But

Subclass.prototype = new Superclass();

is not a very good method either. What if Superclass expects parameters? The better way is to let Subclass.prototype inherit from Superclass.prototype only. This can be done for example with:

function inherits(Child, Parent) {
    var Tmp = function() {};
    Tmp.prototype = Parent.prototype;
    Child.prototype = new Tmp();
    // if the environment supports ES5, instead of the above, you can do
    // Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
}

Which is then to be called with inherits(Subclass, Superclass).

Inside the constructor, you have to call the parent's constructor and pass the instance of the subclass:

function Subclass() {
    Superclass.apply(this, arguments);
    // do some sub class specific initialization here
}
inherits(Subclass, Superclass)

// don't assign a new object to Subclass.prototype
// assign to the current prototype object

Subclass.prototype.some_method = function() {
    // ...
};

All properties which are created in the Superclass constructor are then applied to the new instance of the subclass.

Regarding your second question, I would just put the initialization code into the constructor. Subclass inherits all methods and properties through the prototype chain and the fact that you call the constructor on the new instance.


To be clear, you can use __proto__ if the environment supports it, but since it is not standard it is also not guaranteed that the environment will always support it.

ECMAScript 5 introduced a method specifically for this (creating an object inheriting from another one), which is Object.create. Thus

var foo = {
    __proto__: bar
};

is equivalent to

var foo = Object.create(bar);

There is really no need to use __proto__.

Comments