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:
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?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.
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.
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.
__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.
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.
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.
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();
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 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();
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With