Making subclass with .prototype and __proto__ - 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:
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?
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.

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();

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);
}
}

__proto__ should never be used in your code since it is used by JavaScript Engine implementation to refer back to an objects Class definition prototype.
refer: MDN:__proto__
1>
Subclass.prototype = new Superclass();
is the right way to inherit a super class.
2> if you want to call _init on subclass which will actually execute the Superclass _init method then do not define define it on the SubClass class. When you try to access the _init method on subClass(instance object of SubClass) JS engine tries to look for it on the current obj, if the method is not found it is searched in SubClass proto type chain(i.e. SuperClass)
if you want to call superclass method inside a subclass method then use
Superclass.prototype._init.call(this)
executing the super function in current objects scope. This does the trick of calling the super method within the SubClass method

Related

Class properties in ECMAScript 6 [duplicate]

Currently in ES5 many of us are using the following pattern in frameworks to create classes and class variables, which is comfy:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
In ES6 you can create classes natively, but there is no option to have class variables:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Sadly, the above won't work, as classes only can contain methods.
I understand that I can this.myVar = true in constructor…but I don't want to 'junk' my constructor, especially when I have 20-30+ params for a bigger class.
I was thinking of many ways to handle this issue, but haven't yet found any good ones. (For example: create a ClassConfig handler, and pass a parameter object, which is declared separately from the class. Then the handler would attach to the class. I was thinking about WeakMaps also to integrate, somehow.)
What kind of ideas would you have to handle this situation?
2018 update:
There is now a stage 3 proposal - I am looking forward to make this answer obsolete in a few months.
In the meantime anyone using TypeScript or babel can use the syntax:
varName = value
Inside a class declaration/expression body and it will define a variable. Hopefully in a few months/weeks I'll be able to post an update.
Update: Chrome 74 now ships with this syntax working.
The notes in the ES wiki for the proposal in ES6 (maximally minimal classes) note:
There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property
Class properties and prototype data properties need be created outside the declaration.
Properties specified in a class definition are assigned the same attributes as if they appeared in an object literal.
This means that what you're asking for was considered, and explicitly decided against.
but... why?
Good question. The good people of TC39 want class declarations to declare and define the capabilities of a class. Not its members. An ES6 class declaration defines its contract for its user.
Remember, a class definition defines prototype methods - defining variables on the prototype is generally not something you do.
You can, of course use:
constructor(){
this.foo = bar
}
In the constructor like you suggested. Also see the summary of the consensus.
ES7 and beyond
A new proposal for ES7 is being worked on that allows more concise instance variables through class declarations and expressions - https://esdiscuss.org/topic/es7-property-initializers
Just to add to Benjamin's answer — class variables are possible, but you wouldn't use prototype to set them.
For a true class variable you'd want to do something like the following:
class MyClass {}
MyClass.foo = 'bar';
From within a class method that variable can be accessed as this.constructor.foo (or MyClass.foo).
These class properties would not usually be accessible from to the class instance. i.e. MyClass.foo gives 'bar' but new MyClass().foo is undefined
If you want to also have access to your class variable from an instance, you'll have to additionally define a getter:
class MyClass {
get foo() {
return this.constructor.foo;
}
}
MyClass.foo = 'bar';
I've only tested this with Traceur, but I believe it will work the same in a standard implementation.
JavaScript doesn't really have classes. Even with ES6 we're looking at an object- or prototype-based language rather than a class-based language. In any function X () {}, X.prototype.constructor points back to X.
When the new operator is used on X, a new object is created inheriting X.prototype. Any undefined properties in that new object (including constructor) are looked up from there. We can think of this as generating object and class properties.
Babel supports class variables in ESNext, check this example:
class Foo {
bar = 2
static iha = 'string'
}
const foo = new Foo();
console.log(foo.bar, foo.iha, Foo.bar, Foo.iha);
// 2, undefined, undefined, 'string'
In your example:
class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}
Because of MY_CONST is primitive https://developer.mozilla.org/en-US/docs/Glossary/Primitive we can just do:
class MyClass {
static get MY_CONST() {
return 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
But if MY_CONST is reference type like static get MY_CONST() {return ['string'];} alert output is string, false. In such case delete operator can do the trick:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
And finally for class variable not const:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
static set U_YIN_YANG(value) {
delete MyClass.MY_CONST;
MyClass.MY_CONST = value;
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
set MY_CONST(value) {
this.constructor.MY_CONST = value;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string, true
MyClass.MY_CONST = ['string, 42']
alert(MyClass.MY_CONST);
new MyClass
// alert: string, 42 ; true
Since your issue is mostly stylistic (not wanting to fill up the constructor with a bunch of declarations) it can be solved stylistically as well.
The way I view it, many class based languages have the constructor be a function named after the class name itself. Stylistically we could use that that to make an ES6 class that stylistically still makes sense but does not group the typical actions taking place in the constructor with all the property declarations we're doing. We simply use the actual JS constructor as the "declaration area", then make a class named function that we otherwise treat as the "other constructor stuff" area, calling it at the end of the true constructor.
"use strict";
class MyClass
{
// only declare your properties and then call this.ClassName(); from here
constructor(){
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
this.MyClass();
}
// all sorts of other "constructor" stuff, no longer jumbled with declarations
MyClass() {
doWhatever();
}
}
Both will be called as the new instance is constructed.
Sorta like having 2 constructors where you separate out the declarations and the other constructor actions you want to take, and stylistically makes it not too hard to understand that's what is going on too.
I find it's a nice style to use when dealing with a lot of declarations and/or a lot of actions needing to happen on instantiation and wanting to keep the two ideas distinct from each other.
NOTE: I very purposefully do not use the typical idiomatic ideas of "initializing" (like an init() or initialize() method) because those are often used differently. There is a sort of presumed difference between the idea of constructing and initializing. Working with constructors people know that they're called automatically as part of instantiation. Seeing an init method many people are going to assume without a second glance that they need to be doing something along the form of var mc = MyClass(); mc.init();, because that's how you typically initialize. I'm not trying to add an initialization process for the user of the class, I'm trying to add to the construction process of the class itself.
While some people may do a double-take for a moment, that's actually the bit of the point: it communicates to them that the intent is part of construction, even if that makes them do a bit of a double take and go "that's not how ES6 constructors work" and take a second looking at the actual constructor to go "oh, they call it at the bottom, I see", that's far better than NOT communicating that intent (or incorrectly communicating it) and probably getting a lot of people using it wrong, trying to initialize it from the outside and junk. That's very much intentional to the pattern I suggest.
For those that don't want to follow that pattern, the exact opposite can work too. Farm the declarations out to another function at the beginning. Maybe name it "properties" or "publicProperties" or something. Then put the rest of the stuff in the normal constructor.
"use strict";
class MyClass
{
properties() {
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
}
constructor() {
this.properties();
doWhatever();
}
}
Note that this second method may look cleaner but it also has an inherent problem where properties gets overridden as one class using this method extends another. You'd have to give more unique names to properties to avoid that. My first method does not have this problem because its fake half of the constructor is uniquely named after the class.
As Benjamin said in his answer, TC39 explicitly decided not to include this feature at least for ES2015. However, the consensus seems to be that they will add it in ES2016.
The syntax hasn't been decided yet, but there's a preliminary proposal for ES2016 that will allow you to declare static properties on a class.
Thanks to the magic of babel, you can use this today. Enable the class properties transform according to these instructions and you're good to go. Here's an example of the syntax:
class foo {
static myProp = 'bar'
someFunction() {
console.log(this.myProp)
}
}
This proposal is in a very early state, so be prepared to tweak your syntax as time goes on.
What about the oldschool way?
class MyClass {
constructor(count){
this.countVar = 1 + count;
}
}
MyClass.prototype.foo = "foo";
MyClass.prototype.countVar = 0;
// ...
var o1 = new MyClass(2); o2 = new MyClass(3);
o1.foo = "newFoo";
console.log( o1.foo,o2.foo);
console.log( o1.countVar,o2.countVar);
In constructor you mention only those vars which have to be computed.
I like prototype inheritance for this feature -- it can help to save a lot of memory(in case if there are a lot of never-assigned vars).
[Long thread, not sure if its already listed as an option...].
A simple alternative for contsants only, would be defining the const outside of class.
This will be accessible only from the module itself, unless accompanied with a getter.
This way prototype isn't littered and you get the const.
// will be accessible only from the module itself
const MY_CONST = 'string';
class MyClass {
// optional, if external access is desired
static get MY_CONST(){return MY_CONST;}
// access example
static someMethod(){
console.log(MY_CONST);
}
}
ES7 class member syntax:
ES7 has a solution for 'junking' your constructor function. Here is an example:
class Car {
wheels = 4;
weight = 100;
}
const car = new Car();
console.log(car.wheels, car.weight);
The above example would look the following in ES6:
class Car {
constructor() {
this.wheels = 4;
this.weight = 100;
}
}
const car = new Car();
console.log(car.wheels, car.weight);
Be aware when using this that this syntax might not be supported by all browsers and might have to be transpiled an earlier version of JS.
Bonus: an object factory:
function generateCar(wheels, weight) {
class Car {
constructor() {}
wheels = wheels;
weight = weight;
}
return new Car();
}
const car1 = generateCar(4, 50);
const car2 = generateCar(6, 100);
console.log(car1.wheels, car1.weight);
console.log(car2.wheels, car2.weight);
You can mimic es6 classes behaviour... and use your class variables :)
Look mum... no classes!
// Helper
const $constructor = Symbol();
const $extends = (parent, child) =>
Object.assign(Object.create(parent), child);
const $new = (object, ...args) => {
let instance = Object.create(object);
instance[$constructor].call(instance, ...args);
return instance;
}
const $super = (parent, context, ...args) => {
parent[$constructor].call(context, ...args)
}
// class
var Foo = {
classVariable: true,
// constructor
[$constructor](who){
this.me = who;
this.species = 'fufel';
},
// methods
identify(){
return 'I am ' + this.me;
}
}
// class extends Foo
var Bar = $extends(Foo, {
// constructor
[$constructor](who){
$super(Foo, this, who);
this.subtype = 'barashek';
},
// methods
speak(){
console.log('Hello, ' + this.identify());
},
bark(num){
console.log('Woof');
}
});
var a1 = $new(Foo, 'a1');
var b1 = $new(Bar, 'b1');
console.log(a1, b1);
console.log('b1.classVariable', b1.classVariable);
I put it on GitHub
Still you can't declare any classes like in another programming languages. But you can create as many class variables. But problem is scope of class object. So According to me, Best way OOP Programming in ES6 Javascript:-
class foo{
constructor(){
//decalre your all variables
this.MY_CONST = 3.14;
this.x = 5;
this.y = 7;
// or call another method to declare more variables outside from constructor.
// now create method level object reference and public level property
this.MySelf = this;
// you can also use var modifier rather than property but that is not working good
let self = this.MySelf;
//code .........
}
set MySelf(v){
this.mySelf = v;
}
get MySelf(v){
return this.mySelf;
}
myMethod(cd){
// now use as object reference it in any method of class
let self = this.MySelf;
// now use self as object reference in code
}
}
If its only the cluttering what gives the problem in the constructor why not implement a initialize method that intializes the variables. This is a normal thing to do when the constructor gets to full with unnecessary stuff. Even in typed program languages like C# its normal convention to add an Initialize method to handle that.
Just define a getter.
class MyClass
{
get MY_CONST () { return 'string'; }
constructor ()
{
console.log ("MyClass MY_CONST:", this.MY_CONST);
}
}
var obj = new MyClass();
The way I solved this, which is another option (if you have jQuery available), was to Define the fields in an old-school object and then extend the class with that object. I also didn't want to pepper the constructor with assignments, this appeared to be a neat solution.
function MyClassFields(){
this.createdAt = new Date();
}
MyClassFields.prototype = {
id : '',
type : '',
title : '',
createdAt : null,
};
class MyClass {
constructor() {
$.extend(this,new MyClassFields());
}
};
-- Update Following Bergi's comment.
No JQuery Version:
class SavedSearch {
constructor() {
Object.assign(this,{
id : '',
type : '',
title : '',
createdAt: new Date(),
});
}
}
You still do end up with 'fat' constructor, but at least its all in one class and assigned in one hit.
EDIT #2:
I've now gone full circle and am now assigning values in the constructor, e.g.
class SavedSearch {
constructor() {
this.id = '';
this.type = '';
this.title = '';
this.createdAt = new Date();
}
}
Why? Simple really, using the above plus some JSdoc comments, PHPStorm was able to perform code completion on the properties. Assigning all the vars in one hit was nice, but the inability to code complete the properties, imo, isn't worth the (almost certainly minuscule) performance benefit.
Well, you can declare variables inside the Constructor.
class Foo {
constructor() {
var name = "foo"
this.method = function() {
return name
}
}
}
var foo = new Foo()
foo.method()
Recent browsers as of 2021 (not IE, see MDN browser chart) implement Public class fields which seems to be what you're looking for:
class MyClass {
static foo = 3;
}
console.log(MyClass.foo);
However apparently it's not possible to make this a const: Declaring static constants in ES6 classes?
A static getter looks pretty close:
class MyClass {
static get CONST() {
return 3;
}
}
MyClass.CONST = 4; // property unaffected
console.log(MyClass.CONST);
This is a bit hackish combo of static and get works for me
class ConstantThingy{
static get NO_REENTER__INIT() {
if(ConstantThingy._NO_REENTER__INIT== null){
ConstantThingy._NO_REENTER__INIT = new ConstantThingy(false,true);
}
return ConstantThingy._NO_REENTER__INIT;
}
}
elsewhere used
var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...

Prototype chain: call "super" method over multiple levels

I have got the following prototype chain
SuperSuperClass
SuperClass
Class
each with a method named do.
What is the common approach for calling the respective super class method?
For the moment I use <ClassName>.prototype.__proto__.<methodName>.call(this) but that looks odd.
Using the following code the console prints (as expected):
Class.prototype.do
SuperClass.prototype.do
SuperSuperClass.prototype.do
SuperSuperClass = function SuperSuperClass() {}
SuperSuperClass.prototype.do = function() {
console.log('SuperSuperClass.prototype.do');
};
function SuperClass() {
SuperSuperClass.call(this);
}
SuperClass.prototype = Object.create(SuperSuperClass.prototype);
SuperClass.prototype.constructor = SuperClass;
SuperClass.prototype.do = function() {
console.log('SuperClass.prototype.do');
SuperClass.prototype.__proto__.do.call(this);
};
function Class() {
SuperClass.call(this);
}
Class.prototype = Object.create(SuperClass.prototype);
Class.prototype.constructor = Class;
Class.prototype.do = function() {
console.log('Class.prototype.do');
Class.prototype.__proto__.do.call(this);
};
var objClass = new Class();
objClass.do();
JSFiddle
What is the common approach for calling the respective super class method?
Use <SuperClassName>.prototype.<methodName>.call(this). It's not only shorter, but also has the benefit of working in environments that don't support the non-standard __proto__ property.

ES6 class variable alternatives

Currently in ES5 many of us are using the following pattern in frameworks to create classes and class variables, which is comfy:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
In ES6 you can create classes natively, but there is no option to have class variables:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Sadly, the above won't work, as classes only can contain methods.
I understand that I can this.myVar = true in constructor…but I don't want to 'junk' my constructor, especially when I have 20-30+ params for a bigger class.
I was thinking of many ways to handle this issue, but haven't yet found any good ones. (For example: create a ClassConfig handler, and pass a parameter object, which is declared separately from the class. Then the handler would attach to the class. I was thinking about WeakMaps also to integrate, somehow.)
What kind of ideas would you have to handle this situation?
2018 update:
There is now a stage 3 proposal - I am looking forward to make this answer obsolete in a few months.
In the meantime anyone using TypeScript or babel can use the syntax:
varName = value
Inside a class declaration/expression body and it will define a variable. Hopefully in a few months/weeks I'll be able to post an update.
Update: Chrome 74 now ships with this syntax working.
The notes in the ES wiki for the proposal in ES6 (maximally minimal classes) note:
There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property
Class properties and prototype data properties need be created outside the declaration.
Properties specified in a class definition are assigned the same attributes as if they appeared in an object literal.
This means that what you're asking for was considered, and explicitly decided against.
but... why?
Good question. The good people of TC39 want class declarations to declare and define the capabilities of a class. Not its members. An ES6 class declaration defines its contract for its user.
Remember, a class definition defines prototype methods - defining variables on the prototype is generally not something you do.
You can, of course use:
constructor(){
this.foo = bar
}
In the constructor like you suggested. Also see the summary of the consensus.
ES7 and beyond
A new proposal for ES7 is being worked on that allows more concise instance variables through class declarations and expressions - https://esdiscuss.org/topic/es7-property-initializers
Just to add to Benjamin's answer — class variables are possible, but you wouldn't use prototype to set them.
For a true class variable you'd want to do something like the following:
class MyClass {}
MyClass.foo = 'bar';
From within a class method that variable can be accessed as this.constructor.foo (or MyClass.foo).
These class properties would not usually be accessible from to the class instance. i.e. MyClass.foo gives 'bar' but new MyClass().foo is undefined
If you want to also have access to your class variable from an instance, you'll have to additionally define a getter:
class MyClass {
get foo() {
return this.constructor.foo;
}
}
MyClass.foo = 'bar';
I've only tested this with Traceur, but I believe it will work the same in a standard implementation.
JavaScript doesn't really have classes. Even with ES6 we're looking at an object- or prototype-based language rather than a class-based language. In any function X () {}, X.prototype.constructor points back to X.
When the new operator is used on X, a new object is created inheriting X.prototype. Any undefined properties in that new object (including constructor) are looked up from there. We can think of this as generating object and class properties.
Babel supports class variables in ESNext, check this example:
class Foo {
bar = 2
static iha = 'string'
}
const foo = new Foo();
console.log(foo.bar, foo.iha, Foo.bar, Foo.iha);
// 2, undefined, undefined, 'string'
In your example:
class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}
Because of MY_CONST is primitive https://developer.mozilla.org/en-US/docs/Glossary/Primitive we can just do:
class MyClass {
static get MY_CONST() {
return 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
But if MY_CONST is reference type like static get MY_CONST() {return ['string'];} alert output is string, false. In such case delete operator can do the trick:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
And finally for class variable not const:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
static set U_YIN_YANG(value) {
delete MyClass.MY_CONST;
MyClass.MY_CONST = value;
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
set MY_CONST(value) {
this.constructor.MY_CONST = value;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string, true
MyClass.MY_CONST = ['string, 42']
alert(MyClass.MY_CONST);
new MyClass
// alert: string, 42 ; true
Since your issue is mostly stylistic (not wanting to fill up the constructor with a bunch of declarations) it can be solved stylistically as well.
The way I view it, many class based languages have the constructor be a function named after the class name itself. Stylistically we could use that that to make an ES6 class that stylistically still makes sense but does not group the typical actions taking place in the constructor with all the property declarations we're doing. We simply use the actual JS constructor as the "declaration area", then make a class named function that we otherwise treat as the "other constructor stuff" area, calling it at the end of the true constructor.
"use strict";
class MyClass
{
// only declare your properties and then call this.ClassName(); from here
constructor(){
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
this.MyClass();
}
// all sorts of other "constructor" stuff, no longer jumbled with declarations
MyClass() {
doWhatever();
}
}
Both will be called as the new instance is constructed.
Sorta like having 2 constructors where you separate out the declarations and the other constructor actions you want to take, and stylistically makes it not too hard to understand that's what is going on too.
I find it's a nice style to use when dealing with a lot of declarations and/or a lot of actions needing to happen on instantiation and wanting to keep the two ideas distinct from each other.
NOTE: I very purposefully do not use the typical idiomatic ideas of "initializing" (like an init() or initialize() method) because those are often used differently. There is a sort of presumed difference between the idea of constructing and initializing. Working with constructors people know that they're called automatically as part of instantiation. Seeing an init method many people are going to assume without a second glance that they need to be doing something along the form of var mc = MyClass(); mc.init();, because that's how you typically initialize. I'm not trying to add an initialization process for the user of the class, I'm trying to add to the construction process of the class itself.
While some people may do a double-take for a moment, that's actually the bit of the point: it communicates to them that the intent is part of construction, even if that makes them do a bit of a double take and go "that's not how ES6 constructors work" and take a second looking at the actual constructor to go "oh, they call it at the bottom, I see", that's far better than NOT communicating that intent (or incorrectly communicating it) and probably getting a lot of people using it wrong, trying to initialize it from the outside and junk. That's very much intentional to the pattern I suggest.
For those that don't want to follow that pattern, the exact opposite can work too. Farm the declarations out to another function at the beginning. Maybe name it "properties" or "publicProperties" or something. Then put the rest of the stuff in the normal constructor.
"use strict";
class MyClass
{
properties() {
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
}
constructor() {
this.properties();
doWhatever();
}
}
Note that this second method may look cleaner but it also has an inherent problem where properties gets overridden as one class using this method extends another. You'd have to give more unique names to properties to avoid that. My first method does not have this problem because its fake half of the constructor is uniquely named after the class.
As Benjamin said in his answer, TC39 explicitly decided not to include this feature at least for ES2015. However, the consensus seems to be that they will add it in ES2016.
The syntax hasn't been decided yet, but there's a preliminary proposal for ES2016 that will allow you to declare static properties on a class.
Thanks to the magic of babel, you can use this today. Enable the class properties transform according to these instructions and you're good to go. Here's an example of the syntax:
class foo {
static myProp = 'bar'
someFunction() {
console.log(this.myProp)
}
}
This proposal is in a very early state, so be prepared to tweak your syntax as time goes on.
What about the oldschool way?
class MyClass {
constructor(count){
this.countVar = 1 + count;
}
}
MyClass.prototype.foo = "foo";
MyClass.prototype.countVar = 0;
// ...
var o1 = new MyClass(2); o2 = new MyClass(3);
o1.foo = "newFoo";
console.log( o1.foo,o2.foo);
console.log( o1.countVar,o2.countVar);
In constructor you mention only those vars which have to be computed.
I like prototype inheritance for this feature -- it can help to save a lot of memory(in case if there are a lot of never-assigned vars).
[Long thread, not sure if its already listed as an option...].
A simple alternative for contsants only, would be defining the const outside of class.
This will be accessible only from the module itself, unless accompanied with a getter.
This way prototype isn't littered and you get the const.
// will be accessible only from the module itself
const MY_CONST = 'string';
class MyClass {
// optional, if external access is desired
static get MY_CONST(){return MY_CONST;}
// access example
static someMethod(){
console.log(MY_CONST);
}
}
ES7 class member syntax:
ES7 has a solution for 'junking' your constructor function. Here is an example:
class Car {
wheels = 4;
weight = 100;
}
const car = new Car();
console.log(car.wheels, car.weight);
The above example would look the following in ES6:
class Car {
constructor() {
this.wheels = 4;
this.weight = 100;
}
}
const car = new Car();
console.log(car.wheels, car.weight);
Be aware when using this that this syntax might not be supported by all browsers and might have to be transpiled an earlier version of JS.
Bonus: an object factory:
function generateCar(wheels, weight) {
class Car {
constructor() {}
wheels = wheels;
weight = weight;
}
return new Car();
}
const car1 = generateCar(4, 50);
const car2 = generateCar(6, 100);
console.log(car1.wheels, car1.weight);
console.log(car2.wheels, car2.weight);
You can mimic es6 classes behaviour... and use your class variables :)
Look mum... no classes!
// Helper
const $constructor = Symbol();
const $extends = (parent, child) =>
Object.assign(Object.create(parent), child);
const $new = (object, ...args) => {
let instance = Object.create(object);
instance[$constructor].call(instance, ...args);
return instance;
}
const $super = (parent, context, ...args) => {
parent[$constructor].call(context, ...args)
}
// class
var Foo = {
classVariable: true,
// constructor
[$constructor](who){
this.me = who;
this.species = 'fufel';
},
// methods
identify(){
return 'I am ' + this.me;
}
}
// class extends Foo
var Bar = $extends(Foo, {
// constructor
[$constructor](who){
$super(Foo, this, who);
this.subtype = 'barashek';
},
// methods
speak(){
console.log('Hello, ' + this.identify());
},
bark(num){
console.log('Woof');
}
});
var a1 = $new(Foo, 'a1');
var b1 = $new(Bar, 'b1');
console.log(a1, b1);
console.log('b1.classVariable', b1.classVariable);
I put it on GitHub
Still you can't declare any classes like in another programming languages. But you can create as many class variables. But problem is scope of class object. So According to me, Best way OOP Programming in ES6 Javascript:-
class foo{
constructor(){
//decalre your all variables
this.MY_CONST = 3.14;
this.x = 5;
this.y = 7;
// or call another method to declare more variables outside from constructor.
// now create method level object reference and public level property
this.MySelf = this;
// you can also use var modifier rather than property but that is not working good
let self = this.MySelf;
//code .........
}
set MySelf(v){
this.mySelf = v;
}
get MySelf(v){
return this.mySelf;
}
myMethod(cd){
// now use as object reference it in any method of class
let self = this.MySelf;
// now use self as object reference in code
}
}
If its only the cluttering what gives the problem in the constructor why not implement a initialize method that intializes the variables. This is a normal thing to do when the constructor gets to full with unnecessary stuff. Even in typed program languages like C# its normal convention to add an Initialize method to handle that.
Just define a getter.
class MyClass
{
get MY_CONST () { return 'string'; }
constructor ()
{
console.log ("MyClass MY_CONST:", this.MY_CONST);
}
}
var obj = new MyClass();
The way I solved this, which is another option (if you have jQuery available), was to Define the fields in an old-school object and then extend the class with that object. I also didn't want to pepper the constructor with assignments, this appeared to be a neat solution.
function MyClassFields(){
this.createdAt = new Date();
}
MyClassFields.prototype = {
id : '',
type : '',
title : '',
createdAt : null,
};
class MyClass {
constructor() {
$.extend(this,new MyClassFields());
}
};
-- Update Following Bergi's comment.
No JQuery Version:
class SavedSearch {
constructor() {
Object.assign(this,{
id : '',
type : '',
title : '',
createdAt: new Date(),
});
}
}
You still do end up with 'fat' constructor, but at least its all in one class and assigned in one hit.
EDIT #2:
I've now gone full circle and am now assigning values in the constructor, e.g.
class SavedSearch {
constructor() {
this.id = '';
this.type = '';
this.title = '';
this.createdAt = new Date();
}
}
Why? Simple really, using the above plus some JSdoc comments, PHPStorm was able to perform code completion on the properties. Assigning all the vars in one hit was nice, but the inability to code complete the properties, imo, isn't worth the (almost certainly minuscule) performance benefit.
Well, you can declare variables inside the Constructor.
class Foo {
constructor() {
var name = "foo"
this.method = function() {
return name
}
}
}
var foo = new Foo()
foo.method()
Recent browsers as of 2021 (not IE, see MDN browser chart) implement Public class fields which seems to be what you're looking for:
class MyClass {
static foo = 3;
}
console.log(MyClass.foo);
However apparently it's not possible to make this a const: Declaring static constants in ES6 classes?
A static getter looks pretty close:
class MyClass {
static get CONST() {
return 3;
}
}
MyClass.CONST = 4; // property unaffected
console.log(MyClass.CONST);
This is a bit hackish combo of static and get works for me
class ConstantThingy{
static get NO_REENTER__INIT() {
if(ConstantThingy._NO_REENTER__INIT== null){
ConstantThingy._NO_REENTER__INIT = new ConstantThingy(false,true);
}
return ConstantThingy._NO_REENTER__INIT;
}
}
elsewhere used
var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...

util.inherits - alternative or workaround

I am a n00b in node, and find util.inherits() very useful, except for the fact that it seems to replace the entire prototype of the original object. For instance:
var myClass = function(name){
this._name = name;
};
myClass.prototype = {
(...)
};
util.inherits(myClass, require('events').EventEmitter);
seems to erase my original prototype.
That brings me two inconveniences:
1 - I have to declare add properties to my prototype after calling inherits,
var myClass = function(name){
this._name = name;
};
util.inherits(myClass, require('events').EventEmitter);
myClass.prototype.prop1 = function(){...};
myClass.prototype.prop2 = function(){...};
and, most important, i think i cannot inherit from two or more different classes.
Can anyone explain to me why this makes sense and what would be a good way to work around this?
Thanks
It does not make sense that you have to declare your prototype after util.inherits(). My guess is util.inherits originated as an internal-use-only method, tailored only for the limited internal use-cases it was initially intended for, which at some point got published for general usage. The util module is written in pure JS, so it is very easy to implement your own version of util.inherit that preserves your prototype. Here's the original util.inherit source:
exports.inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
As for the multiple inheritance, that's going to be a much more difficult problem to tackle, as Javascript's prototype inheritance is not really suited for multiple inheritance at all. Each instance has only a single internal [[Prototype]] property, which is used to look up members that are not found in the actual instance. You could merge the prototypes of two separate "parent classes" into a single prototype, but you will then lose the inheritance to their parents, and you will lose the ability to change the parent prototype and have all children see the change.
You should first inherit, after the function definition, and then implement your object. Also don't forget to call the superclass constructor in your class's constructor.
Function A(y){
this.y = x;
}
Function B(x,y){
this.x = x;
A.call(this,y);
}
util.inherits(B,A);
B.prototype.mythodA = function() {
//do something
}
As of node version 5.0.0, util.inherits has been changed to support the behaviour you are looking for using the setPrototypeOf method:
FirstBase.js
function FirstBase(firstBaseProp){
this.firstBaseProp = firstBaseProp;
}
FirstBase.prototype.getFirstBaseProp = function(){
return this.firstBaseProp;
};
module.exports = FirstBase;
SecondBase.js
var FirstBase = require('./FirstBase.js'),
util = require('util');
function SecondBase(firstBaseProp, secondBaseProp){
this.secondBaseProp = secondBaseProp;
SecondBase.super_.apply(this, arguments);
}
SecondBase.prototype.getSecondBaseProp = function(){
return this.secondBaseProp;
};
util.inherits(SecondBase, FirstBase);
module.exports = SecondBase;
ThirdBase.js
var SecondBase = require('./SecondBase.js'),
util = require('util');
function ThirdBase(firstBaseProp, secondBaseProp, thirdBaseProp){
this.thirdBaseProp = thirdBaseProp;
ThirdBase.super_.apply(this, arguments);
}
ThirdBase.prototype.getThirdBase = function(){
return this.thirdBaseProp;
};
util.inherits(ThirdBase, SecondBase);
module.exports = ThirdBase;
instance.js
var ThirdBase = require('./ThirdBase.js');
var instance = new ThirdBase('first', 'second', 'third');
// With node < 5.0.0 (Object.create)
console.log(instance.getFirstBaseProp()); // first
console.log(instance.getSecondBaseProp()); // undefined
console.log(instance.getThirdBase()); // undefined
// With node >= 5.0.0 (Object.setPrototypeOf)
console.log(instance.getFirstBaseProp()); // first
console.log(instance.getSecondBaseProp()); // second
console.log(instance.getThirdBase()); // third
If you're running an older version of node that supports setPrototypeOf (0.12.x does), you can just export util.inherits and use it as an internal utility function.
I very much wanted the same thing and was unhappy with extend, so I created this extension to the already useful util.inherits method:
var util = require('util');
module.exports = {
inherits : function(sub, sup, proto) {
util.inherits(sub, sup);
if (typeof proto !== 'undefined') {
Object.keys(proto).forEach(function(key) {
sub.prototype[key] = proto[key];
});
}
}
};
I put this in my project's ./util/index.js and then do this to use it:
var EventEmitter = require('events').EventEmitter;
var util = require('./util');
function Foo() {
EventEmitter.call(this);
}
util.inherits(Foo, EventEmitter, {
bar : function(){
console.log(this instanceof EventEmitter); // true
}
});
Maybe I'll publish this if I find it's robust and useful more and more. I just barely implemented it myself, so I'm giving it a test run.
Let me know what you think!
NOTE: This does override methods on either of the classes with the ones defined in the proto hash at the end. Just be aware of that.
Inherits-ex library is a browser-friendly enhanced inheritance fully compatible with standard node.js inherits with dynamic inheritance or creation.
static inheritance
multi-inheritances(inheritance chain) supports
inherits at anytime -- here is your wanted
you can not declare method/property before inherits in the standard way for it will replace the prototype object.
duplication inheritance check
Es6 Class supports
more helper functions
isInheritedFrom(ctor, superCtor|superCtorName) Check the ctor whether inherited from superCtor
mixin(ctor, superCtor|superCtor[]) Mixin the methods and properties of the SuperCtor: Clone(Copy) all superCtor's properties(methods) to ctor.
isMixinedFrom
createCtor
createObject
createFunction

Javascript inheritance: call super-constructor or use prototype chain?

Quite recently I read about JavaScript call usage in MDC
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call
one linke of the example shown below, I still don't understand.
Why are they using inheritance here like this
Prod_dept.prototype = new Product();
is this necessary? Because there is a call to the super-constructor in
Prod_dept()
anyway, like this
Product.call
is this just out of common behaviour? When is it better to use call for the super-constructor or use the prototype chain?
function Product(name, value){
this.name = name;
if(value >= 1000)
this.value = 999;
else
this.value = value;
}
function Prod_dept(name, value, dept){
this.dept = dept;
Product.call(this, name, value);
}
Prod_dept.prototype = new Product();
// since 5 is less than 1000, value is set
cheese = new Prod_dept("feta", 5, "food");
// since 5000 is above 1000, value will be 999
car = new Prod_dept("honda", 5000, "auto");
Thanks for making things clearer
The answer to the real question is that you need to do both:
Setting the prototype to an instance of the parent initializes the prototype chain (inheritance), this is done only once (since the prototype object is shared).
Calling the parent's constructor initializes the object itself, this is done with every instantiation (you can pass different parameters each time you construct it).
Therefore, you should not call the parent's constructor when setting up inheritance. Only when instantiating an object that inherits from another.
Chris Morgan's answer is almost complete, missing a small detail (constructor property). Let me suggest a method to setup inheritance.
function extend(base, sub) {
// Avoid instantiating the base class just to setup inheritance
// Also, do a recursive merge of two prototypes, so we don't overwrite
// the existing prototype, but still maintain the inheritance chain
// Thanks to #ccnokes
var origProto = sub.prototype;
sub.prototype = Object.create(base.prototype);
for (var key in origProto) {
sub.prototype[key] = origProto[key];
}
// The constructor property was set wrong, let's fix it
Object.defineProperty(sub.prototype, 'constructor', {
enumerable: false,
value: sub
});
}
// Let's try this
function Animal(name) {
this.name = name;
}
Animal.prototype = {
sayMyName: function() {
console.log(this.getWordsToSay() + " " + this.name);
},
getWordsToSay: function() {
// Abstract
}
}
function Dog(name) {
// Call the parent's constructor
Animal.call(this, name);
}
Dog.prototype = {
getWordsToSay: function(){
return "Ruff Ruff";
}
}
// Setup the prototype chain the right way
extend(Animal, Dog);
// Here is where the Dog (and Animal) constructors are called
var dog = new Dog("Lassie");
dog.sayMyName(); // Outputs Ruff Ruff Lassie
console.log(dog instanceof Animal); // true
console.log(dog.constructor); // Dog
See my blog post for even further syntactic sugar when creating classes. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
Technique copied from Ext-JS and http://www.uselesspickles.com/class_library/ and a comment from https://stackoverflow.com/users/1397311/ccnokes
The ideal way to do it is to not do Prod_dept.prototype = new Product();, because this calls the Product constructor. So the ideal way is to clone it except for the constructor, something like this:
function Product(...) {
...
}
var tmp = function(){};
tmp.prototype = Product.prototype;
function Prod_dept(...) {
Product.call(this, ...);
}
Prod_dept.prototype = new tmp();
Prod_dept.prototype.constructor = Prod_dept;
Then the super constructor is called at construction time, which is what you want, because then you can pass the parameters, too.
If you look at things like the Google Closure Library you'll see that's how they do it.
If you have done Object Oriented Programming in JavaScript, you will know that you can create a class as follows:
Person = function(id, name, age){
this.id = id;
this.name = name;
this.age = age;
alert('A new person has been accepted');
}
So far our class person only has two properties and we are going to give it some methods. A clean way of doing this is
to use its 'prototype' object.
Starting from JavaScript 1.1, the prototype object was introduced in JavaScript. This is a built in object that
simplifies the process of adding custom properties and methods to all instances of an object.
Let's add 2 methods to our class using its 'prototype' object as follows:
Person.prototype = {
/** wake person up */
wake_up: function() {
alert('I am awake');
},
/** retrieve person's age */
get_age: function() {
return this.age;
}
}
Now we have defined our class Person. What if we wanted to define another class called Manager which inherits some properties from Person. There is no point redefining all this properties again when we define our Manager class, we can just set it to inherit from the class Person.
JavaScript doesn't have built in inheritance but we can use a technique to implement inheritance as follows:
Inheritance_Manager = {};//We create an inheritance manager class (the name is arbitrary)
Now let's give our inheritance class a method called extend which takes the baseClass and subClassas arguments.
Within the extend method, we will create an inner class called inheritance function inheritance() { }. The reason why we are using this inner
class is to avoid confusion between the baseClass and subClass prototypes.
Next we make the prototype of our inheritance class point to the baseClass prototype as with the following code:
inheritance.prototype = baseClass. prototype;
Then we copy the inheritance prototype into the subClass prototype as follows: subClass.prototype = new inheritance();
The next thing is to specify the constructor for our subClass as follows: subClass.prototype.constructor = subClass;
Once finished with our subClass prototyping, we can specify the next two lines of code to set some base class pointers.
subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;
Here is the full code for our extend function:
Inheritance_Manager.extend = function(subClass, baseClass) {
function inheritance() { }
inheritance.prototype = baseClass.prototype;
subClass.prototype = new inheritance();
subClass.prototype.constructor = subClass;
subClass.baseConstructor = baseClass;
subClass.superClass = baseClass.prototype;
}
Now that we have implemented our inheritance, we can start using it to extend our classes. In this case we are going to
extend our Person class into a Manager class as follows:
We define the Manager class
Manager = function(id, name, age, salary) {
Person.baseConstructor.call(this, id, name, age);
this.salary = salary;
alert('A manager has been registered.');
}
we make it inherit form Person
Inheritance_Manager.extend(Manager, Person);
If you noticed, we have just called the extend method of our Inheritance_Manager class and passed the subClass Manager in our case and then the baseClass Person. Note that the order is very important here. If you swap them, the inheritance
will not work as you intended if at all.
Also note that you will need to specify this inheritance before you can actually define our subClass.
Now let us define our subClass:
We can add more methods as the one below. Our Manager class will always have the methods and properties defined in the Person class because it inherits from it.
Manager.prototype.lead = function(){
alert('I am a good leader');
}
Now to test it let us create two objects, one from the class Person and one from the inherited class Manager:
var p = new Person(1, 'Joe Tester', 26);
var pm = new Manager(1, 'Joe Tester', 26, '20.000');
Feel free to get full code and more comments at:
http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

Categories

Resources