I am making an application using JavaScript. One thing that I noticed is that when I call super() from child class constructor, I cannot use child class fields.
For example:
class Parent {
constructor() {
console.log(this.x);
}
}
class Child extends Parent {
x = 5;
constructor() {
super();
}
}
Output: undefined
As you see, the parent constructor cannot access the x field of the child constructor. There are similar questions on stackoverflow like:
Access JavaScript class property in parent class
The solution they give is to use an init() method on the Parent class. I realized that every method other than the constructor of the Parent class has access to child fields.
class Parent {
init() {
console.log(this.x);
}
}
class Child extends Parent {
x = 5;
constructor() {
super();
this.init();
}
}
Output: 5
However, I can also get access to the x field by using Timeout with delay of 0:
class Parent {
constructor() {
setTimout(() => console.log(this.x), 0);
}
}
class Child extends Parent {
x = 5;
constructor() {
super();
}
}
Output: 5
All the answers on stackoverflow give the solution, but nobody explains how and why this happens. So my question is:
1. Why the constructor doesn't have access to fields of the child class?
2. Why adding a timeout function gives access to the fields of the child class?
Related
I've recently started to learn about Classes in javascript and while reading some really interesting stuff I thought of trying some of my own ideas.
If you have a parent class of Parent in which you have a method of logSomething```` and a child class ofChild, with which you doclass Child extends Parent, how can you then execute the inherited method from the parent class,logSomething```, inside of the child class?
If you define a method inside of the Child class and add this.logSomething() to that method, whenever the method from the child class is called, the inherited logSomething function will indeed run, but apart from that I haven't found any way of executing the logSomething directly inside of that child class.
I've tried this.logSomething(), I've tried adding it to a object, self executing (IIFE) function and everything I could thing of but to no result.
class Parent {
constructor() {}
logSomething() {
console.log('I am logging something')
}
}
class Child extends Paren {
logSomething() // This does not work
}
Currently doing this does not work, if throws a error referring to the fact that it things your trying to define a function.
I know it should be possible in some way, if I'm not mistaking React uses something similar with life-cycle methods right? Such as componentWillMount.
How would one go about doing this?
First error is that you are extending Paren instead of Parent.
Also you cannot just throw a random statement inside in a class. It needs to be inside a function.
If you want it to run whenever you create an instance of that class it should be inside the constructor or a function that gets called by it. (note that you need to call super() at the start of the constructor.
Finally, you still need to use this.logSomething or this.logSomething
class Parent {
constructor() {}
logSomething() {
console.log('I am logging something');
}
}
class Child extends Parent {
constructor() {
super();
this.logSomething(); // Will use Parent#logSomething since Child doesn't contain logSomething
super.logSomething(); // Will use Parent#logSomething
}
}
new Child();
class Parent {
constructor() {}
logSomething() {
console.log('Parent Logging Called');
}
}
class Child extends Parent {
constructor() {
super();
this.logSomething(); // Will call Child#logSomething
super.logSomething(); // Will call Parent#logSomething
}
logSomething() {
console.log('Child Logging Called');
}
}
new Child();
You could also do this:
class Parent {
constructor() {}
logSomething() {
console.log('Parent Logging Called');
}
}
class Child extends Parent {
logSomething() {
console.log('Child Logging Called and ...');
// Careful not use this.logSomething, unless if you are planning on making a recursive function
super.logSomething();
}
}
new Child().logSomething();
You can call any function or use any property of the parent class using this, as long as the new class doesn't have its own definition for that property.
Look here for more information.
class Parent {
constructor() {}
logSomething() {
console.log('I am logging something')
}
}
class Child extends Parent {
logSomething() {
super.logSomething(); // Call parent function
}
}
a) you can't call a function there, you can call a function within a function declared in a class
b) you need to use this.logSomething()
example:
class Parent {
constructor() {}
logSomething() {
console.log('I am logging something')
}
}
class Child extends Parent {
fn() {
this.logSomething() // This does work
}
}
new Child().fn()
See other answers for when fn is called logSomething in the child class - then you'd need super.logSomething() to call the "parent" logSomething instead of the child logSomething
Is It good/bad practice to call a child method from a parent class?
class Parent {
constructor() {
// if 'autoPlay' exists (was implemented) in chain
if (this.autoPlay) {
this.autoPlay(); // execute from parent
}
}
}
class ChildA extends Parent {
autoPlay() {
console.log('Child');
}
}
class ChildB extends Parent {
// 'autoPlay' wasn't implemented
}
const childA = new ChildA();
const childB = new ChildB();
Is it a good practice to call a child method from a parent class?
Yes, it's a totally normal practise. The parent class just calls some method of the instance, and if the child class has overridden the method then the child method is called. However, you usually wouldn't do such a "has my instance defined this method" test, you just would call it. If you want to do nothing by default, just define an empty method (like in #scipper's answer). If you want to make the method abstract (force child classes to override it), you can either leave it undefined or define a method that throws an appropriate exception.
Is is a bad practice to call a child method from a parent constructor?
Yes. Don't do that. (It's a problem in all languages).
The purpose of a constructor is to initialise the instance and nothing else. Leave the invocations of side effects to the caller. This will ensure that all child constructors will finish their initialisation as well.
A contrived example:
class Parent {
autoPlay() {
this.play("automatically "); // call child method
}
play(x) {
console.log(x+"playing default from "+this.constructor.name);
}
}
class ChildA extends Parent {
// does not override play
}
class ChildB extends Parent {
constructor(song) {
super();
this.song = song;
}
play(x) {
console.log(x+"playing "+this.song+" from ChildB");
}
}
const child1 = new ChildA();
child1.autoPlay();
const child2 = new ChildB("'Yeah'");
child2.autoPlay();
Notice how that would not work if the Parent constructor did call autoplay. If you don't like to need an extra method call everywhere after the instantiation, use a helper function. It might even be a static method:
class Parent {
autoPlay() { … }
play { … }
static createAndAutoPlay(...args) {
const instance = new this(...args);
instance.autoPlay();
return instance;
}
}
…
const child1 = ChildA.createAndAutoPlay();
const child2 = ChildB.createAndAutoPlay("'Yeah'");
It would be better style to define an empty implementation of autoPlay in the Parent class, and override it in the child.
class Parent {
constructor() {
this.autoPlay();
}
autoPlay() {
}
}
class Child extends Parent {
autoPlay() {
console.log('Child');
}
}
const child = new Child();
I have a class Parent (defined using the class declaration, though I'm aware this is primarily syntactic sugar), and multiple classes (Child1, Child2, etc) that extend it. The child classes have a static property assigned to them (outside of their declarations - as far as I'm aware there is no way to assign a static property within a class declaration).
I would like to access the a static value of any child class from the parent class, such as in the method getStaticValue().
class Parent {
constructor() {
//Do parent stuff
}
getStaticValue() {
return "The value of staticValue is " + this.staticValue;
}
}
class Child1 extends Parent {
constructor() {
super();
//Do child1 stuff
}
}
Child1.staticValue = "Child1";
class Child2 extends Parent {
constructor() {
super();
//Do child2 stuff
}
}
Child2.staticValue = "Child2";
I want to access the value of staticValue of any arbitrary child from the parent class, however attempting to do so as written above always returns undefined. In other words:
let parentStaticValue = new Parent().getStaticValue();
//Desired output = "The value of staticValue is undefined"
//Actual output = "The value of staticValue is undefined"
let child1StaticValue = new Child1().getStaticValue();
//Desired output = "The value of staticValue is Child1"
//Actual output = "The value of staticValue is undefined"
let child2StaticValue = new Child2().getStaticValue();
//Desired output = "The value of staticValue is Child2"
//Actual output = "The value of staticValue is undefined"
Is there a way to access the a static value of a child class from the parent class, without having to know the name of the child class in every case?
You can use the constructor property of a class instance to access static properties held as properties of the instance's constructor function, as in:
class Parent {
constructor() {
//Do parent stuff
}
getStaticValue() {
return this.constructor.staticValue;
}
}
Warning
The constructor property of an object is inherited from the prototype chain.
Class syntax made the prototype object property of a class constructor's function object non-configurable (good), but left the constructor property of this same prototype object writable (bad).
NEVER change the constructor value of class constructors' prototype objects if you know what's good for you want class extensions to work correctly.
Demo:
class Parent {
constructor() {
//Do parent stuff
}
getStaticValue() {
return "The value of staticValue is " + this.constructor.staticValue;
}
}
class Child1 extends Parent {
constructor() {
super();
//Do child1 stuff
}
}
Child1.staticValue = "Child1";
class Child2 extends Parent {
constructor() {
super();
//Do child2 stuff
}
}
Child2.staticValue = "Child2";
console.log(new Parent().getStaticValue());
console.log(new Child1().getStaticValue());
console.log(new Child2().getStaticValue());
You can pass your static value to the parent constructor using super() in a child class:
class Parent {
constructor(childStaticValue) { // receive the value from the children class
this.staticValue = childStaticValue // and assign it to the local variable
}
getStaticValue() {
return "The value of staticValue is " + this.staticValue;
}
}
class Child1 extends Parent {
constructor() {
super(Child1.staticValue); // pass the value to the parent class
//Do child1 stuff
}
}
Child1.staticValue = "Child1";
class Child2 extends Parent {
constructor() {
super(Child2.staticValue); // pass the value to the parent class
//Do child2 stuff
}
}
Child2.staticValue = "Child2";
If I create the following classes, is there any way in Class1 to detect when the instance is actually one of Class2, without knowing anything about Class2?
i.e. Can Class1 tell when it's the parent class being extended?
class Class1 {
constructor() {
// Code to detect whether parent here
}
}
class Class2 extends Class1 {
constructor() {
super();
}
}
This is what new.target was made for - it gives you the constructor that new was invoked with. So
class Class1 {
constructor() {
if (new.target != Class1) {
// Class1 is used as a parent class
}
}
}
When I do a console.log(object) I expect to see the name of the object's class. So it seems rather unexpected that a child class carries the name of its parent.
"use strict";
class Parent {
constructor () {
}
}
class Child extends Parent {
constructor () {
super();
}
}
class Grandchild extends Child {
constructor () {
super();
}
}
var grandchild = new Grandchild();
console.log(grandchild); // Parent {}
console.log(grandchild.constructor.name); // Grandchild
console.log(grandchild instanceof Parent); // true
console.log(grandchild instanceof Child); // true
console.log(JSON.stringify(grandchild)); // {}
Is this intended behaviour? Is it the console.log that's messing it up, or does JavaScript consider instances of any descendant class to be, first and foremost, the instance of the root level class?
console is not standard, as you can see in its MDN entry. The standard way to get the class name of an instance in ES6 is to use instance.contructor.name. This is stated in the spec.