I have a base class defined as an object literal, with methods defined inside constructor's scope. Like this:
var NodeMappingAbstract = function NodeMappingAbstract() {
this.mapToContent = function (obj) {
throw new Error('Not implemented');
};
this.getInfo = function (data) {
throw new Error('Not implemented');
};
this.normalizeUri = function normalizeUri(uri) {
return uriNormalizer.normalize(uri);
};
};
How do I extend theNodeMappingAbstract with ES6 class syntax? I attempted using extends and super():
class SomeMapping extends NodeMappingAbstract{
constructor(){
super();
}
mapToContent(rawElement) {
console.warn('mapContent called');
return rawElement;
}
}
But when I instantiate it and call mapToContent like this:
let m = new SomeMapping();
m.mapToContent();
I get Error: Not implemented, which means the implementation of the NodeMappingAbstract is used.
The problem here is that you are attaching the methods directly on an instance of NodeMappingAbstract (this), and not the prototype of the function.
When JavaScript performs a lookup for an object property it first inspects the objects immediate own properties, which are what are made available when you assign something to this. Only if it does not find that property immediately available in this way will it turn to the prototype. So, although you are extending the NodeMappingAbstract and defining methods on the prototype (via the class sugar), you are not actually overriding the properties that are assigned to this on instantiation inside NodeMappingAbstract.
So, move the method declarations outside of the function body and onto its prototype:
var NodeMappingAbstract = function NodeMappingAbstract () {};
NodeMappingAbstract.prototype.mapToContent = function (obj) {
throw new Error('Not implemented');
};
NodeMappingAbstract.prototype.getInfo = function (data) {
throw new Error('Not implemented');
};
NodeMappingAbstract.prototype.normalizeUri = function normalizeUri (uri) {
return uriNormalizer.normalize(uri);
};
You should put your methods on the prototype of your ES5 class (as you always should).
If you define them in the constructor, and create them as instance properties (which shadow anything inherited from the prototype), you will have to do the same when overwriting them:
class SomeMapping extends NodeMappingAbstract{
constructor(){
super();
this.mapToContent = function(rawElement) {
console.warn('mapContent called');
return rawElement;
};
}
}
Related
I am confused should I declare methods inside or outside the constructor method in a class definition?
How does it help to declare to methods inside the constructor function in a class definition?
What is the disadvantage of declaring methods outside the constructor function?
class animal {
constructor(name, type, color){
this.name = name;
this.type = type;
this.color = color;
this.sound = () => {console.log(this.name,this.type,this.color)}
}
}
class animal {
constructor(name, type, color){
this.name = name;
this.type = type;
this.color = color;
}
sound = () => {console.log(this.name,this.type,this.color)}
}
Creating objects in either way, results in the similar objects. What am I missing?
The definition of functions inside the constructor was mostly used to store and access private variables in calls like this:
function MyClass() {
let privateVar = { something: "usefull" };
this.getPrivateVar = function getPrivateVar() { return privateVar; };
}
new MyClass().getPrivateVar(); // { something: "usefull" }
Just because calls produce a similar result, doesn't mean that they have an equal behavior. They can act completly differently depending on how they get called or how they where defined.
This counts especially for arrow functions as they don't have a this binding. They access the closest this providing environment, when defined.
class MyClass {}
MyClass.prototype.arrow = () => this;
console.log(new MyClass().arrow() == globalThis); // true
The answers from How does JavaScript .prototype work? explain pretty well how prototype is working and for classes every property and function defined inside it, will be stored on constructor.prototype, with some exceptions like the static and #private properties.
Private properties are only available and accessible inside the class and not in an inherited or an extended class and can't be accessed by a classic prototype.
class MyClass {
#private = "example";
doSomethingWithMyPrivate() { return this.#private }
}
MyClass.prototype.getThePrivate = function() { return this.#private };
new MyClass().doSomethingWithMyPrivate(); // example
// Both produce the same SyntaxError:
// Private field '#private' must be declared in an enclosing class
new MyClass().#private;
new MyClass().getThePrivate();
Static properties will be placed on the constructor directly.
class MyClass {
static doSomething() { console.log("Okay!") }
}
MyClass.doSomething(); // Okay!
new MyClass().doSomething(); // TypeError: doSomething is not a function
Every instance of a class will access the prototype references or the inheriting objects prototype until any object has a match with the property-name, if not it returns undefined or throw a more specific error depending on the situation.
From this answer:
Javascript has a mechanism when looking up properties on Objects which is called 'prototypal inheritance', here is what is basically does:
First is checked if the property is located on the Object itself. If so this property is returned.
If the property is not located on the object itself it will 'climb up the protochain'. It basically looks at the object referred to by the proto property. There it checks if the property is available on the object referred to by proto
If the property isn't located on the proto object it will climb up the proto chain all the way up to Object object.
If it cannot find the property nowhere on the object and its prototype chain it will return undefined.
So if you define the functions inside the constructor, each instance gets a new function assigned (they won't have the same reference). Which means, depending on the class (new animal).sound == (new animal).sound, the statement will return true (your 2nd example) or false (your 1st example) and the prototypal inheritance will take place or not.
// the old way
function Foo() {
if (this instanceof Foo) {
this.fx = () => {};
} else throw new TypeError(`Constructor Foo cannot be invoked without 'new'`);
}
Foo.prototype.pfx = function() {};
console.log((new Foo).fx, (new Foo).fx == (new Foo).fx); // arrow ƒ, false
console.log((new Foo).pfx, (new Foo).pfx == (new Foo).pfx); // ƒ, true
// the class way
class Bar {
constructor() { // similar too "ƒ Foo() {}" with the addition of super ƒ
this.fx = () => {};
}
// this is almost like Bar.prototype.pfx = ...
pfx() {}
}
// works like before
Bar.prototype.moo = function moo() {};
console.log((new Bar).fx, (new Bar).fx == (new Bar).fx); // arrow ƒ, false
console.log((new Bar).pfx, (new Bar).pfx == (new Bar).pfx); // ƒ, true
console.log((new Bar).moo, (new Bar).moo == (new Bar).moo); // ƒ, true
If you define properties as mentioned in your example #1, you can "override" other methods and this may cause trouble with private properties as mentioned above.
class Foo {
constructor() {
// just for the example
this.fx = Foo.prototype.fx;
}
fx() { console.log("I am broken"); };
}
class Bar extends Foo {
fx() { console.log("I fix something"); }
}
class Moo extends Bar {
constructor() {
super();
delete this.fx;
}
}
/*
* If instance has no own property called fx the default
* behavior will take place:
* Test the construtor.prototype if it has one and execute this.
* If not, look at the inheriting classes in descending order
* (Moo > Bar > Foo > Object)
* But bar.fx() => has an own property called fx which takes place
*/
let bar = new Bar();
bar.fx(); // I am broken
// none of the inheriting classes has a toJSON ƒ,
// so Object.prototype.toJSON will be called.
console.log(bar.toJSON == Object.prototype.toJSON) // true
// the Moo constructor deletes the own property after the Foo constructor was evaluated, so Bar.prototype.fx takes place
new Moo().fx() // I fix something
I have an abstract class that implements some methods on its prototype and I want to create an instance directly of this class without subclassing it.
I can instantiate that class by creating a Proxy and trapping construct and it seems to work. Properties of the new instance are set correctly but I have a hard time calling its methods.
function AbstractNumbers(...args) {
if (new.target === AbstractNumbers) {
throw new Error('Cannot instantiate abstract class');
}
this.numbers = args;
}
AbstractNumbers.prototype.showNumbers = function() { console.log(this.numbers); }
const AbstractNumbersProxy = new Proxy(AbstractNumbers, {
construct(target, args) {
// change 3rd argument to bypass new.target test
return Reflect.construct(target, args, function() {});
}
});
const n = new AbstractNumbersProxy(1, 2, 3);
// set prototype back to AbstractNumbers
Object.setPrototypeOf(n, AbstractNumbers);
// n.__proto__ shows the correct prototype
console.log(n.__proto__);
// property n.numbers is set correctly
console.log(n.numbers);
// calling its prototype method fail
n.showNumbers();
How can I properly instantiate that abstract class so that I am able to call its methods?
In
// set prototype back to AbstractNumbers
Object.setPrototypeOf(n, AbstractNumbers);
you've set the prototype back to the constructor function instead of its prototype property. Try
Object.setPrototypeOf(n, AbstractNumbers.prototype);
instead:
function AbstractNumbers(...args) {
if (new.target === AbstractNumbers) {
throw new Error('Cannot instantiate abstract class');
}
this.numbers = args;
}
AbstractNumbers.prototype.showNumbers = function() { console.log(this.numbers); }
const AbstractNumbersProxy = new Proxy(AbstractNumbers, {
construct(target, args) {
// change 3rd argument to bypass new.target test
return Reflect.construct(target, args, function() {});
}
});
const n = new AbstractNumbersProxy(1, 2, 3);
// set prototype back to AbstractNumbers
Object.setPrototypeOf(n, AbstractNumbers.prototype);
// n.__proto__ shows the correct prototype
console.log(n.__proto__);
// property n.numbers is set correctly
console.log(n.numbers);
// calling its prototype method fail
n.showNumbers();
Please do not ask me to investigate what you are doing.
I'm trying to inherit class "EventEmitter" and a pre defined class "Person", here is the code
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduces() {
return `My name is ${this.name}. I am ${this.age} years old.`;
}
};
\\here comes the mixin part
function mix(...mixins) {
class Mix {}
for (let mixin of mixins) {
copyProperties(Mix, mixin);
copyProperties(Mix.prototype, mixin.prototype);
}
return Mix;
}
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if (key !== "constructor" && key !== "prototype" && key !== "name") {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
I intend to create a new class 'PersonWithEmitter', and still call the constructor like below:
class PersonWithEmitter extends mix(Person,EventEmitter){
constructor(name,age){
super(name,age)
\\do something else
}
Here comes the issue, when I create a new instance of 'PersonWithEmitter' like this let someOne = new PersonWithEmitter("Tom",21), will not get what I what, In the new class, I want to use this.name and this.age, which is still undefined.
So how can I change my code, So the new class can both have its parent's methods and only class "Person"'s constructor?
Pardon me for my broken English.
In many cases multiple inheritance in JavaScript indicates wrong design decision. It may result in hacky objects that don't behave as they should. The way it is done should always be determined by particular objects. In some cases a shallow copy of own properties is needed, in another the entire prototype chain should be traversed. Composition over inheritance is often a better choice.
The problem in the code above is that class constructors are not called. Mix has empty constructor. This is the reason why PersonWithEmitter doesn't work as expected.
Multiple constructor function calls can generally be stacked like:
function Foo(...args) {
let _this = this;
_this = Bar.apply(_this, args);
_this = Baz.apply(_this, args);
return _this;
}
This won't work if Bar or Baz is ES6 class because it contains a mechanism that prevents it from being called without new. In this case they should be instantiated:
function Foo(...args) {
copyProperties(this, new Bar(...args));
copyProperties(this, new Baz(...args));
}
Static and prototype properties may also be copied to Foo like is shown in code above.
If the case is narrowed down to Node.js EventEmitter, it can be handled like a special case. Its implementation is certain and stable. It is already known that EventEmitter does initialization in constructor, it has a shallow prototype chain and property descriptors. So it likely should be:
class Foo extends Bar {
constructor(...args) {
super(...args)
EventEmitter.call(this);
// or
// EventEmitter.init.call(this);
}
copyProperties(Foo.prototype, EventEmitter.prototype);
How would we polyfill es6 class methods into ES5?
I am reading a book and it says the following:
class Ninja {
constructor(name) {
this.name = name;
}
swingSword() {
return true;
}
}
is the same as
function Ninja(name) {
this.name = name;
}
Ninja.prototype.swingSword = function() {
return true;
};
I am just asking why are we adding the swingSword on the prototype and not inside the constructor function?
Because the function should be on the object and not on the prototype chain.
Am i right or wrong?
It should be on the prototype, methods are not per-instance data. Can't think of any language that implements it that way, the whole idea of classes is to have a whole class of objects that have the same set of methods.
If it was put it inside the constructor function, it would be a unique function per instance made with the constructor. e.g, 1000 objects == 1000 functions, per "method".
Adding the function to just the object would only work for a Ninja. To create a class that extends Ninja, for example Kunoichi, you would normally copy the Ninja prototype. Unfortunately, because swingSword is not in the prototype, your Kunoichi cannot swing swords.
You must add the function in prototype to allow the class to be extended.
If we add a method to the prototype, only one instance of that method exists in memory, and it’s shared between all objects created from the constructor.
If we add the swingSword method directly to the Ninja constructor function, then every object would have its own copy of that method, taking up more memory.
var $class = function ($superclass, config) {
// All classes have a superclass with the root
// of this $class hierarchy being Object.
var self = function (config) {
// Object.assign or $.extend or ...
config && Object.assign(this, config);
};
self.prototype = new $superclass(config);
return self;
};
var A = $class(Object, {
sayWhat: "Hello, I'm an A",
say: function () {
console.log(this.sayWhat);
}
});
var B = $class(A, {
sayWhat: "Hello, I'm a B"
});
var C = $class(B, {
say: function () {
console.log("C SAYS: " + this.sayWhat);
},
superSay: function () {
// how to call a superclass method
B.prototype.say.call(this);
}
});
var a = new A();
a.say(); // Hello, I'm an A
var b = new B();
b.say(); // Hello, I'm a B
var c = new C();
c.say(); // C SAYS: Hello, I'm a B
// create a "one-off" object
var d = new C({
sayWhat: "I'm special!",
say: function () {
console.log("hey!");
}
});
d.say(); // hey!
d.superSay(); // I'm special!
C.prototype.say.call(d); // C SAYS: I'm special!
Is it possible to extend a class in ES6 without calling the super method to invoke the parent class?
EDIT: The question might be misleading. Is it the standard that we have to call super() or am I missing something?
For example:
class Character {
constructor(){
console.log('invoke character');
}
}
class Hero extends Character{
constructor(){
super(); // exception thrown here when not called
console.log('invoke hero');
}
}
var hero = new Hero();
When I'm not calling super() on the derived class I'm getting a scope problem -> this is not defined
I'm running this with iojs --harmony in v2.3.0
The rules for ES2015 (ES6) classes basically come down to:
In a child class constructor, this cannot be used until super is called.
ES6 class constructors MUST call super if they are subclasses, or they must explicitly return some object to take the place of the one that was not initialized.
This comes down to two important sections of the ES2015 spec.
Section 8.1.1.3.4 defines the logic to decide what this is in the function. The important part for classes is that it is possible for this be in an "uninitialized" state, and when in this state, attempting to use this will throw an exception.
Section 9.2.2, [[Construct]], which defines the behavior of functions called via new or super. When calling a base class constructor, this is initialized at step #8 of [[Construct]], but for all other cases, this is uninitialized. At the end of construction, GetThisBinding is called, so if super has not been called yet (thus initializing this), or an explicit replacement object was not returned, the final line of the constructor call will throw an exception.
The new ES6 class syntax is only an other notation for "old" ES5 "classes" with prototypes. Therefore you cannot instantiate a specific class without setting its prototype (the base class).
Thats like putting cheese on your sandwich without making it. Also you cannot put cheese before making the sandwich, so...
...using this keyword before calling the super class with super() is not allowed, too.
// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
constructor() {
super();
this.supplement = "Cheese";
}
}
// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
super();
}
}
// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
constructor() {
this.supplement = "Cheese";
}
}
If you don’t specify a constructor for a base class, the following definition is used:
constructor() {}
For derived classes, the following default constructor is used:
constructor(...args) {
super(...args);
}
EDIT: Found this on developer.mozilla.org:
When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.
Source
There have been multiple answers and comments stating that super MUST be the first line inside constructor. That is simply wrong. #loganfsmyth answer has the required references of the requirements, but it boil down to:
Inheriting (extends) constructor must call super before using this and before returning even if this isn't used
See fragment below (works in Chrome...) to see why it might make sense to have statements (without using this) before calling super.
'use strict';
var id = 1;
function idgen() {
return 'ID:' + id++;
}
class Base {
constructor(id) {
this.id = id;
}
toString() { return JSON.stringify(this); }
}
class Derived1 extends Base {
constructor() {
var anID = idgen() + ':Derived1';
super(anID);
this.derivedProp = this.baseProp * 2;
}
}
alert(new Derived1());
You can omit super() in your subclass, if you omit the constructor altogether in your subclass. A 'hidden' default constructor will be included automatically in your subclass. However, if you do include the constructor in your subclass, super() must be called in that constructor.
class A{
constructor(){
this.name = 'hello';
}
}
class B extends A{
constructor(){
// console.log(this.name); // ReferenceError
super();
console.log(this.name);
}
}
class C extends B{} // see? no super(). no constructor()
var x = new B; // hello
var y = new C; // hello
Read this for more information.
The answer by justyourimage is the easiest way, but his example is a little bloated. Here's the generic version:
class Base {
constructor(){
return this._constructor(...arguments);
}
_constructor(){
// just use this as the constructor, no super() restrictions
}
}
class Ext extends Base {
_constructor(){ // _constructor is automatically called, like the real constructor
this.is = "easy"; // no need to call super();
}
}
Don't extend the real constructor(), just use the fake _constructor() for the instantiation logic.
Note, this solution makes debugging annoying because you have to step into an extra method for every instantiation.
Just registered to post this solution since the answers here don't satisfy me the least since there is actually a simple way around this. Adjust your class-creation pattern to overwrite your logic in a sub-method while using only the super constructor and forward the constructors arguments to it.
As in you do not create an constructor in your subclasses per se but only reference to an method that is overridden in the respective subclass.
That means you set yourself free from the constructor functionality enforced upon you and refrain to a regular method - that can be overridden and doesn't enforce super() upon you letting yourself the choice if, where and how you want to call super (fully optional) e.g.:
super.ObjectConstructor(...)
class Observable {
constructor() {
return this.ObjectConstructor(arguments);
}
ObjectConstructor(defaultValue, options) {
this.obj = { type: "Observable" };
console.log("Observable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class ArrayObservable extends Observable {
ObjectConstructor(defaultValue, options, someMoreOptions) {
this.obj = { type: "ArrayObservable" };
console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
class DomainObservable extends ArrayObservable {
ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
this.obj = super.ObjectConstructor(defaultValue, options);
console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
console.log("obj is:", this.obj);
return this.obj;
}
}
var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");
cheers!
#Bergi mentioned new.target.prototype, but I was looking for a concrete example proving that you can access this (or better, the reference to the object the client code is creating with new, see below) without having to call super() at all.
Talk is cheap, show me the code... So here is an example:
class A { // Parent
constructor() {
this.a = 123;
}
parentMethod() {
console.log("parentMethod()");
}
}
class B extends A { // Child
constructor() {
var obj = Object.create(new.target.prototype)
// You can interact with obj, which is effectively your `this` here, before returning
// it to the caller.
return obj;
}
childMethod(obj) {
console.log('childMethod()');
console.log('this === obj ?', this === obj)
console.log('obj instanceof A ?', obj instanceof A);
console.log('obj instanceof B ?', obj instanceof B);
}
}
b = new B()
b.parentMethod()
b.childMethod(b)
Which will output:
parentMethod()
childMethod()
this === obj ? true
obj instanceof A ? true
obj instanceof B ? true
So you can see that we are effectively creating an object of type B (the child class) which is also an object of type A (its parent class) and within the childMethod() of child B we have this pointing to the object obj which we created in B's constructor with Object.create(new.target.prototype).
And all this without caring about super at all.
This leverages the fact that in JS a constructor can return a completely different object when the client code constructs a new instance with new.
Hope this helps someone.
Try:
class Character {
constructor(){
if(Object.getPrototypeOf(this) === Character.prototype){
console.log('invoke character');
}
}
}
class Hero extends Character{
constructor(){
super(); // throws exception when not called
console.log('invoke hero');
}
}
var hero = new Hero();
console.log('now let\'s invoke Character');
var char = new Character();
Demo
I would recommend to use OODK-JS if you intend to develop following OOP concepts.
OODK(function($, _){
var Character = $.class(function ($, µ, _){
$.public(function __initialize(){
$.log('invoke character');
});
});
var Hero = $.extends(Character).class(function ($, µ, _){
$.public(function __initialize(){
$.super.__initialize();
$.log('invoke hero');
});
});
var hero = $.new(Hero);
});
Simple solution: I think its clear no need for explanation.
class ParentClass() {
constructor(skipConstructor = false) { // default value is false
if(skipConstructor) return;
// code here only gets executed when 'super()' is called with false
}
}
class SubClass extends ParentClass {
constructor() {
super(true) // true for skipping ParentClass's constructor.
// code
}
}