Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making subclass with .prototype and __proto__

Tags:

javascript

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.

like image 471
mathematical.coffee Avatar asked May 14 '12 08:05

mathematical.coffee


People also ask

What does __ proto __ mean in JavaScript?

The __proto__ getter function exposes the value of the internal [[Prototype]] of an object. For objects created using an object literal, this value is Object. prototype . For objects created using array literals, this value is Array.

What is the difference between __ proto __ and prototype?

The prototype property is set to function when it is declared. All the functions have a prototype property. proto property that is set to an object when it is created using a new keyword. All objects behavior newly created have proto properties.

Is __ proto __ deprecated?

__proto__ property has been deprecated as of ECMAScript 3.1 and shouldn't be used in the code. Use Object. getPrototypeOf and Object. setPrototypeOf instead.

What is the difference between an internal prototype and the .prototype property of a function?

In reality, the only true difference between prototype and __proto__ is that the former is a property of a class constructor, while the latter is a property of a class instance.


2 Answers

As already said, don't use __proto__. It's a non-standard property. (It's standardized for JavaScript in browsers now. Still don't use it.) But

Subclass.prototype = new Superclass(); // Don't do this

is not a very good method either. What if Superclass expects parameters?

You have better options.

ES2015 and above

class handles all of this plumbing for you; complete example:

class Superclass {
    constructor(superProperty) {
        this.superProperty = superProperty;
    }
    method() {
        console.log("Superclass's method says: " + this.superProperty);
    }
}
class Subclass extends Superclass {
    constructor(superProperty, subProperty) {
        super(superProperty);
        this.subProperty = subProperty;
    }
    method() {
        super.method(); // Optional, do it if you want super's logic done
        console.log("Subclass's method says: " + this.subProperty);
    }
}

let o = new Subclass("foo", "bar");
console.log("superProperty", o.superProperty);
console.log("subProperty", o.subProperty);
console.log("method():");
o.method();

ES5 and earlier

Let Subclass.prototype inherit from Superclass.prototype only. This can be done for example with ES5's Object.create:

Subclass.prototype = Object.create(Superclass.prototype);
Subclass.prototype.constructor = Subclass;

And then in Subclass, you call Superclass with this referring to the object so it has a chance to initialize:

function Subclass() {
    Superclass.call(this); // Pass along any args needed
}

Full example:

function Superclass(superProperty) {
    this.superProperty = superProperty;
}
Superclass.prototype.method = function() {
    console.log("Superclass's method says: " + this.superProperty);
};
function Subclass(superProperty, subProperty) {
    Superclass.call(this, superProperty);
    this.subProperty = subProperty;
}
Subclass.prototype = Object.create(Superclass.prototype);
Subclass.prototype.constructor = Subclass;

Subclass.prototype.method = function() {
    Superclass.prototype.method.call(this); // Optional, do it if you want super's logic done
    console.log("Subclass's method says: " + this.subProperty);
};

var o = new Subclass("foo", "bar");
console.log("superProperty", o.superProperty);
console.log("subProperty", o.subProperty);
console.log("method():");
o.method();

ES3 and earlier

ES3 and earlier don't have Object.create, but you can easily write a function that sets up the prototype for you in much the same way:

function objectCreate(proto) {
    var ctor = function() { };
    ctor.prototype = proto;
    return new ctor;
}

(Note: You could half-shim Object.create by creating one that takes only one argument, but the multiple-argument version of Object.create cannot be shimmed, so it would be giving other code on the page the wrong idea if it also uses Object.create.)

Then you do the same thing as our ES5 example:

Full example:

function objectCreate(proto) {
    var ctor = function() { };
    ctor.prototype = proto;
    return new ctor;
}

function Superclass(superProperty) {
    this.superProperty = superProperty;
}
Superclass.prototype.method = function() {
    console.log("Superclass's method says: " + this.superProperty);
};
function Subclass(superProperty, subProperty) {
    Superclass.call(this, superProperty);
    this.subProperty = subProperty;
}
Subclass.prototype = objectCreate(Superclass.prototype);
Subclass.prototype.constructor = Subclass;

Subclass.prototype.method = function() {
    Superclass.prototype.method.call(this); // Optional, do it if you want super's logic done
    console.log("Subclass's method says: " + this.subProperty);
};

var o = new Subclass("foo", "bar");
console.log("superProperty", o.superProperty);
console.log("subProperty", o.subProperty);
console.log("method():");
o.method();
like image 108
Felix Kling Avatar answered Sep 19 '22 12:09

Felix Kling


Although __proto__ has now been standardized as a required extension to JavaScript when used on web browsers, there's no reason to use it when setting up inheritance hierarchies.

Instead, use Object.create (an ES5 function, the key parts of which for our purposes here could be shimmed if you really needed to support antiquated browser).

Here's an example for when

var BaseClass = function() {
};

var SubClass = function() {
    // Important that SubClass give BaseClass a chance to init here
    BaseClass.call(this/*, args, if, necessary, here*/);

    // ...
};

// Make `SubClass`'s `prototype` an object that inherits from
// `BaseClass`'s `prototype`:
SubClass.prototype = Object.create(BaseClass.prototype);

// Fix up the `constructor` property
SubClass.prototype.constructor = SubClass;

That's it.

If BaseClass's constructor requires an argument, you pass it the argument in SubClass:

Here's an example for when

var BaseClass = function(arg) {
    this.prop = arg;
};

var SubClass = function(baseArg) {   // Can accept it, or provide a
    BaseClass.call(this, baseArg);   // hardcoded one here

    // ...
};

SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

Of course, as of ES2015 (aka "ES6"), you can just use class (and transpile if necessary).

class BaseClass {
    constructor(arg) {
        this.prop = arg;
    }
}

class SubClass extends BaseClass {
    constructor(baseArg) {
        super(baseArg);
    }
}
like image 28
T.J. Crowder Avatar answered Sep 21 '22 12:09

T.J. Crowder