How to access an object instantiated as a parameter? - javascript

Assume that class Cheddar inherits from a class which expects an object as a parameter; essentially composition:
class Brand {
constructor(b) {
this.brand = b;
}
getBrand() {
return this.brand;
}
}
class Cheese {
constructor(brand_obj) {
// do stuff...
}
}
1
class Cheddar extends Cheese {
constructor(b) {
super(new Brand(b)); // <-- HERE, I'm a "one-liner" kinda guy :D
}
}
Now when I instantiate:
let snack = new Cheddar("Cabot Clothbound");
I can't access the Brand object, because it was created as an argument.
So, I tried to create the Brand and put it on the object before calling super, like this:
2
class Cheddar extends Cheese {
constructor(b) {
this.brand = new Brand(b);
super(this.brand);
}
}
...which results in the following error:
'this' is not allowed before super()
Grr.. so, I could do it this way:
3
class Cheddar extends Cheese {
constructor(b) {
let myBrand = new Brand(b);
super(myBrand);
this.brand = myBrand;
}
getBrand() {
return this.brand.getBrand();
}
}
And I can now happily access the methods on the cheese object like so:
let snack = new Cheese("Cabot Clothbound");
console.log(snack.getBrand()); //-> Cabot Clothbound
...but, it's getting messy. I want to be a "one-liner guy."
Anyway to access the object created as an argument to this constructor, or can I possibly structure things a bit differently to make this easier? I feel like I'm working too hard here. Thx, Keith :^D

You are not finishing the construction of Cheese. Your // do stuff should include saving the brand_obj if you want to access it from subclasses.
When you call super(new Brand(b)) the brand object will end up in the super class's constructor. But you aren't doing anything with it there. If save the brand as a property on the super class it will be available to the subclass:
class Brand {
constructor(b) {
this.brand = b;
}
getBrand() {
return this.brand;
}
}
class Cheese {
constructor(brand_obj) {
this.brand = brand_obj // <= save this!!
}
}
class Cheddar extends Cheese {
constructor(b) {
super(new Brand(b));
console.log("brand in cheddar constructor", this.brand) //<== now you can use it
}
}
let snack = new Cheddar("Cabot Clothbound");
console.log("Brand on instance: ", snack.brand)

Related

How can I call the method of the great grandfather in Javascript?

I would like to call a method from the great grandfather to the Athlete class,
How can I do that?
I tried using super.printSentence but that did not work,
Is this the correct way to invoke the method, super.printPositon() in the Athlete class?
Any suggestion on how to invoke this printSentence method?
class Person {
constructor(name) {
this.name = name;
}
printName() {
console.log(this.name);
}
}
class TeamMate extends Person {
constructor(name) {
super(name);
}
printSentence() {
console.log(super.printName(), "is an excellent teammate!" );
}
}
class SoccerPlayer extends TeamMate {
constructor(name, teamMateName, position) {
super(name, teamMateName);
this.teamMateName = teamMateName;
this.position = position;
}
printPositon() {
console.log("Positon: ", position);
}
}
class Athlete extends SoccerPlayer{
constructor(name, teamMateName, position, sport) {
super(name, teamMateName, position);
this.sport = sport;
}
printSport() {
console.log("Favorite sport: ", this.sport);
}
//If Athlete class extends from SoccerPlayer and SoccerPlayer extends from
// the TeamMate class, how can I invoke the printSentence method
// from the TeamMate class in this current Athlete class?
printGreatGrandFatherMethod() {
return this.printSentence()
}
}
const soccerPlayer = new Athlete('PLAYER1', 'Frederick', 'Defender', 'Soccer');
console.log(soccerPlayer.printGreatGrandFatherMethod());
Why am I getting undefined for the name field?
Just to this.printSentence()
In inheritance (prototype or not) the this has access to all the methods.
Unless you are using private method like this:
class ClassWithPrivateMethod {
#privateMethod() {
return 'hello world';
}
}
If you think about it, if a Person has a name any class that inherited from Person will also have a member name. This is also true to any instance of a class that inherits from Person. for example:
const soccerPlayer = new SoccerPlayer('PLAYER1', 'MATE NAME', '1');
console.log(soccerPlayer.name); // Prints `PLAYER1`
printSentence() doesn't return a value, so return this.printSentence() will return undefined. And since that's what printGreatGrandFatherMethod returns, therefore console.log(soccerPlayer.printGreatGrandFatherMethod()); will log undefined.
Same goes for printName() which doesn't return a value either and therefore console.log(super.printName()) will log undefined

Should i call the parent class method with super() in the child class

I have a question about super() in javascript.
Should i call the parent class method 'stringy()' with super() in the child class or i can use it like this:
function solve() {
class Melon {
constructor(weight, melonSort) {
if (new.target === Melon) {
throw new Error('Abstract class cannot be instantiated directly.')
}
this.weight = weight;
this.melonSort = melonSort;
this._elementIndex = weight * Number(melonSort.length);
}
stringy() {
return `Element: ${this.element}\nSort: ${this.melonSort}\nElement Index: ${this._elementIndex}`
}
}
class Watermelon extends Melon {
constructor(weight, melonSort) {
super(weight, melonSort);
this.element = 'Water';
}
}
or
function solve() {
class Melon {
constructor(weight, melonSort) {
if (new.target === Melon) {
throw new Error('Abstract class cannot be instantiated directly.')
}
this.weight = weight;
this.melonSort = melonSort;
this._elementIndex = weight * Number(melonSort.length);
}
stringy() {
return `Element: ${this.element}\nSort: ${this.melonSort}\nElement Index: ${this._elementIndex}`
}
}
class Watermelon extends Melon {
constructor(weight, melonSort) {
super(weight, melonSort);
this.element = 'Water';
}
stringy(){
return super.stringy();
}
}
which one is correct and what is the difference.
There's no need to include stringy in Watermelon unless you want to change the behavior. Watermelon instances will inherit the Melon version if you don't. Your two versions of Watermelon are very nearly identical, and calls to stringy on instances of either version will create and return the same string. If stringy used arguments, your overwritten one would need to be sure to pass all arguments it received along, but stringy doesn't use any, so...
(Just for completeness: The only slight difference is that in your second version of Watermelon, there is a Watermelon.prototype.stringy function, whereas in your first version there isn't. It's fine that there isn't, because Watermelon.prototype inherits from Melon.prototype, which has stringy.)

ES6: Why do extending classes need to explicitly call super? [duplicate]

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

Referring to child class in function inherited from parent

I'm have method a that decorates a given array of objects:
class Vehicle {
static fromArray(vals) {
return vals.map((v) => { return new Vehicle(v); });
}
}
...I would like to extend that to work on a series of child-classes:
class Boat extends Vehicle {
}
class Car extends Vehicle {
}
Car.fromArray([{name: 'volvo'}]) // should return Car objects, not Vehicle
What do I replace Vehicle with in the parent? Or do I have to override fromArray in the child objects?
You could slightly modify your code:
'use strict'
class Vehicle {
static fromArray(vals) {
var constr = this; // <-- don't need this if you use an arrow function below
return vals.map(function(v) { return new constr(v); });
}
}
class Boat extends Vehicle {
}
class Car extends Vehicle {
drive() { console.log('driving'); }
}
var base = Vehicle.fromArray([1])[0];
var car = Car.fromArray([1])[0];
car.drive();
//base.drive(); // <-- will throw exception
The key here is that for static methods, the this variable is set to the type itself, so you can new this() to create an object of the same type.
Note: this is tested on Chrome (V8), so should work on Node / iojs, BUT, I'm not 100% sure this is according to spec.
In static methods, the this context is typically the constructor function itself, so you can just use that instead of referring to your Vehicle explicitly:
class Vehicle {
static fromArray(vals) {
return vals.map(v => new this(v));
}
}
Notice that static methods are not bound, so if you reference them as a callback or so you have to care about getting this right.

How to extend a class without having to use super in ES6?

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

Categories

Resources