ES6 Accessing child class's `this` when overriding a function in javascript - javascript

I have following code:
class BaseClass {
constructor() {
// BELOW LINE CALLS OVERRIDDEN FUNCTION BUT WITH `this` OF BaseClass
this.preHook();
}
// THIS FUNCTION IS BEING OVERRIDDEN IN CHILD CLASS
preHook(data) {
return data;
}
}
class ChildClass extends BaseClass {
constructor() {
super();
this.someKey = 'someValueFromChild';
}
preHook(data) {
console.log('this.someKey', this.someKey); //IT PRINTS undefined
}
}
var c = new ChildClass();
OUTPUT:
this.someKey undefined
Expecting
this.someKey someValueFromChild
So in BaseClass's constructer i am calling a function this.preHook() which is getting overridden in ChildClass, when we initialise ChildClass it calls BaseClass's constructer and then it calls this.preHook's overridden version , but instead of printing someValueFromChild which is defined in child class, it is printing undefined (it is using this of parent class instead of child`.
How can i make it to use this of child class.

How can i make it to use this of child class.
There is no "this of child class." There is only one object (backed by its prototype objects). this within BaseClass and this within ChildClass both refer to the same object.
The reason accessing this.someKey results in undefined in that code is that the child constructor doesn't set someKey until after the parent constructor is finished — and the parent constructor calls preHook, so preHook is called before the child constructor sets someKey. So there is no someKey property at all as of when preHook tries to read it from the object. You'd see this if you stepped through the code with a debugger. Here's an annotated version showing what happens when:
class BaseClass {
constructor() {
console.log("BaseClass constructor start");
this.preHook();
console.log("BaseClass constructor finish");
}
// THIS FUNCTION IS BEING OVERRIDDEN IN CHILD CLASS
preHook(data) {
console.log("BaseClass.preHook called");
return data;
}
}
class ChildClass extends BaseClass {
constructor() {
console.log("ChildClass constructor start");
super();
console.log("ChildClass constructor setting `someKey`");
this.someKey = 'someValueFromChild';
console.log("ChildClass constructor finish");
}
preHook(data) {
console.log("ChildClass.preHook called");
console.log('this.someKey', this.someKey);
}
}
var c = new ChildClass();
.as-console-wrapper {
max-height: 100% !important;
}
Calling overrideable methods from a constructor is a well-established anti-pattern — and this is part of the reason. :-) Instead, don't have the parent constructor call preHook. Do it after construction.
There are workarounds, but the best option by far is to not have the BaseClass constructor call preHook at all. How you do that in your code obviously depends on the greater context of your code, so I can't give you an example.

There is race condition that the consequence of natural limitations of ES6 classes. this should always appear after super() in child constructor. Since this.preHook() is called in parent constructor (super), there's no way how this.someKey can be assigned in constructor and appear during this.preHook() call.
Otherwise this.preHook() call should happen in child constructor only, this defies the concept of hooks and prevents child class from being properly extended.
In order to avoid race condition, someKey should be defined on class prototype, thus super and this precedence is no longer a problem:
class ChildClass extends BaseClass {
constructor() {
super();
}
get someKey() {
return 'someValueFromChild';
}
...
}
The alternative that allows to maintain proper class lifecycle is to implement hooks on application level, like it's usually done in JS frameworks. Hookable classes should be instantiated through certain interface and never directly, i.e:
function instantiateWithHooks(cls, args) {
const instance = new cls(...args);
instance.preHook();
return instance;
}
As explained in another answer, when child class is instantiated, there is only one this, which is (usually) an instance of child class.

Related

Why need to use super method in this code

I am new to JS and was learning classes and the inheritance but I faced the confusion of why to use the super method in child class. Here is the code:
class Animal {
constructor(name, weight) {
this.name = name;
this.weight = weight;
}
eat() {
return `${this.name} is eating!`;
}
sleep() {
return `${this.name} is going to sleep!`;
}
wakeUp() {
return `${this.name} is waking up!`;
}
}
class Gorilla extends Animal {
constructor(name, weight) {
super(name, weight);
}
climbTrees() {
return `${this.name} is climbing trees!`;
}
poundChest() {
return `${this.name} is pounding its chest!`;
}
showVigour() {
return `${super.eat()} ${this.poundChest()}`;
}
dailyRoutine() {
return `${super.wakeUp()} ${this.poundChest()} ${super.eat()} ${super.sleep()}`;
}
}
console.log((new Gorilla("Jonas", 12)).dailyRoutine());
As you can see the gorilla class uses super method and if I remove that super method I get an error but WHY? I just cannot when to use super method and why?
The call to super() initializes the super, or parent, class. The parent class and it's members must be initialized first, as the child class usually depends on them to some extent. You cannot inherit a class and not initialize the parent because you may not be able to access members that the child class depends on.
super() inside the constructor allows you to pass arguments to the parent constructor.
class Parent {
constructor(weNeedThis) {
console.log(weNeedThis);
}
}
const parent = new Parent("When constructing, it's easy to pass the argument");
class Child {
constructor() {
// But how do we pass an argument to the parent's constructor here?
}
}
So in other words, we need super() sometimes to pass arguments to the parent constructor.
if I remove that super method I get an error but WHY?
That's for consistency. If some constructors would call super() and others wouldn't, it is unclear when and how the parents constructor runs. Therefore it is specified, that if you provide your own constructor, you have to call super before you can access this. When super() gets called, the parent constructor gets run with the arguments provided, this gets initialized and is accessible from then on.
super gives you access to the parent class. In your specific example, you need to call the Animal constructor and provide the parameters that needs. The reason that it throws the error without it is that the Animal portion of your object can't be initialized without running through the constructor.
super will also give you reference to the other functions and properties within the parent class.
Check out the official documentation to get more information.

In two ES6 Parent-Child classes, is there a way to execute some code in parent class immediately after child constructor executes?

Example:
class Parent { constructor() {} }
class Child { constructor() { super(); someChildCode(); } }
I just want to execute some code after someChildCode(). Yes, I can place it there, but requirement is not placing that code there.
Because there are too many (n) child classes, and only one Parent, thus I want to not duplicate code (n) times.
P.S. I want clean code solution as simple as new Child() when creating child objects.
P.S. Downvoter, care to explain? I realise that the task may not be solvable, that is why I have asked the question, to be sure if that's the case.
If you really need the exact same code to run in every child class upon construction, then that common code probably belongs in the parent constructor. But maybe you need some child-specific code to run before the common code, in which case you still have an issue.
Either way, one possible solution would be not to override the constructor in the child classes at all, but instead let them inherit the parent constructor, which contains any common code as well as pre- and post- hooks you can override in child classes as necessary. For example:
class Parent {
// do not override the constructor, conceptually "final"
constructor() {
this.beforeCommonConstructorCode();
// add any common constructor code here
console.log("Common constructor code")
this.afterCommonConstructorCode();
}
// override this in child classes as needed
public beforeCommonConstructorCode() {
// empty
}
// override this in child classes as needed
public afterCommonConstructorCode() {
// empty
}
}
new Parent();
// in console:
// >> Common constructor code
And when you subclass Parent, you leave the constructor alone and add code to the appropriate methods:
class Child extends Parent {
// remember, do not override constructor
public beforeCommonConstructorCode() {
console.log("Child pre-constructor code")
}
public afterCommonConstructorCode() {
console.log("Child post-constructor code")
}
}
new Child();
// in console:
// >> Child pre-constructor code
// >> Common constructor code
// >> Child post-constructor code
Note that TypeScript will not prevent child classes from overriding the constructor (there is no "final" keyword or functionality) so discipline is needed. Otherwise, I think this behaves the way you like; you are freed from having to place common code in each subclass.
Hope that helps. Good luck.
If I understand this correctly, you can have an intermediate parent to achieve this given your code is generic and does not depend on child.
class Parent(){
}
class InterMediateParent extends Parent{
constructor(){
super();
someCode();
}
}
class Child extends InterMediateParent{
constructor(){
super();
}
}
You can add that function to the Parent prototype so that only one copy is maintained across all child objects and call that function in child's constructor.
class Parent {
constructor() {
}
}
Parent.prototype.someCommonCode = function() {
console.log("Common code for all Parent as well as it's child objects");
}
class Child extends Parent {
constructor() {
//Child class code
super();
//Some child code
this.someChildCode();
//Call Parent's common function
super.someCommonCode();
}
someChildCode() {
console.log("Some child code");
}
}
let child1 = new Child();
let child2 = new Child();

Reactjs: In JavaScript, class methods are not bound by default

I'm following reactjs handling-events documentation, and I came across this:
In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
What I understand from this quotation is that this doesn't designate the current instantiation in every class methods.
However, if I consider the code below, the method editState can access the property state of the extended class Parent through this. Which would make false the previous quotation, as this is bound to all the class mehtods.
class Parent {
constructor() {
this.state = false;
}
setState(newState) {
this.state = newState;
}
}
class Child extends Parent {
editState() {
this.setState(true);
}
}
let c = new Child;
c.editState();
console.log(c.state);
What am I missing here?
When a function is used as an event handler, its 'this' is set to the element the event fired from.
As a DOM event handler
I think the React documentation might be misleading. The React component below MyClass is a derived class (aka child class) of React.Component. The constructor method is a special method within javascript ES6 which is used for creating and initializing an object created with a class. React uses the constructor to pass props to the component when the object is created. Base classes (aka parent classes) are created bound to their own this, but the derived classes do not have their own this by default. The derived class must call super(props) which binds the this from the base class down into the derived class.
class MyClass extends React.Component{
constructor(props) {
super(props)
}
}
Here is the relevant information from the link provided by user376830's answer. This is straight from the javascript documentation.
Class context
The behavior of this in classes and functions is similar, since classes are functions under the hood. But there are some differences and caveats.
Within a class constructor, this is a regular object. All non-static methods within the class are added to the prototype of this:
class Example {
constructor() {
const proto = Object.getPrototypeOf(this);
console.log(Object.getOwnPropertyNames(proto));
}
first(){}
second(){}
static third(){}
}
new Example(); // ['constructor', 'first', 'second']
Note: Static methods are not properties of this. They are properties of the class itself.
Derived classes
Unlike base class constructors, derived constructors have no initial this binding. Calling super() creates a this binding within the constructor and essentially has the effect of evaluating the following line of code, where Base is the inherited class:
this = new Base();
Warning: Referring to this before calling super() will throw an error.
Derived classes must not return before calling super(), unless they return an Object or have no constructor at all.
class Base {}
class Good extends Base {}
class AlsoGood extends Base {
constructor() {
return {a: 5};
}
}
class Bad extends Base {
constructor() {}
}
new Good();
new AlsoGood();
new Bad(); // ReferenceError
William's answer is the most correct after following the link he provided. I'm just posting this to save anybody the time. It is quite a read. Regarding this question when you pass a function as a variable, you need to be careful about the context it is going to be used in.
From my understanding, when a method is fired from a class it's 'this' gets bound to the class object. When it is fired from an event handler it's 'this' gets bound to the button element that fired the event. Since button elements aren't javascript objects, they do not have a 'this' property hence the undefined value.

Cannot use child class' properties within parent class constructor via Babel's transform-class-properties

When extending a parent class and declaring the child class' properties via Babel's 'transform-class-properties' plugin, any class properties of the child class are not accessible via the parent class' constructor method.
class One {
internal = 1;
constructor() {
console.log('constructor internal', this.internal);
}
}
class Two extends One {
internal = 2;
}
new Two();
In the example above, 'constructor internal 1' will be output to the console. When looking at the compiled code this is obvious as to why, with the parent class being executed first and the resulting object then being integrated with the child class.
Apologies if this is by design, but it has confused me as the following code works in the way I am expecting within non-constructor methods (so the boot() method references the child class' 'internal' property value):
class One {
internal = 1;
constructor() {
console.log('constructor internal', this.internal);
}
boot() {
console.log('boot internal', this.internal);
}
}
class Two extends One {
internal = 2;
constructor() {
super();
this.boot();
}
}
new Two();
So, even when calling a method declared on the parent class, it will inherit the child class' properties. It is just constructor methods which seemingly do not behave as expected (at least by me - again, apologies if this is wrongly interpreted, but there are no caveats listed on the relative Babel page.)
Thank you.
I think that is natural. If you wish to override the parent class's property init value, you should do it in the derived class's constructor.
class Two extends One {
constructor() {
// Call parent constructor.
super();
// Override here.
this.internal = 2;
}
}
Hope it helps. Happy coding (:
loganfsmyth answered my question very clearly here: https://phabricator.babeljs.io/T7567 (Thanks!)
This is indeed expected behavior. Babel's implementation is a little incorrect because in a perfect world this would also throw an error, because you shouldn't have the same property defined in a parent and a child, but we don't do that right at the moment.
Class properties are initialized when:
For base classes, before the constructor executes
For child classes, at the end of super()
and when a given constructor initializes it's bindings, it uses that classes bindings, it doesn't know of anything in child classes. That means your One class knows the value should be 1, so that's what it sets.

Defining a Class in ES2015, what is the constructor method and why is it essential?

I am in the process of learning ES2015 and I'm not fully understanding everything about classes. To my understanding you define a class like this:
class Parent {
constructor() {
// ...
}
}
and a subclass like this:
(where super() must be invoked to initially run the constructor from the parent class).
class subClass extends Parent {
constructor(name, description, url) {
// subclass must call super to invoke the parent's constructor method
super();
}
}
What exactly is the constructor method, why is it important, and why does a parents constructor needs to be invoked when creating an instance of a subclass?
When you create a new instance of a class, the constructor method will be called with the arguments you passed through. In this function, you put any code to construct the instance of your class, like initializing properties.
class Person{
constructor(name){
this.name = name;
}
sayHi(){
alert(`${this.name} says hello!`);
}
}
let person = new Person('Jerry');//this param will be send to the constructor method
person.sayHi();
The result of this code will be an alert saying "Jerry says hello!".
Although, the constructor method is not required. The following code will work too.
class Person{
sayHi(){
alert("Someone says hello!");
}
}
let person = new Person();
person.sayHi();
In case you have a subclass, you might want to call the constructor method of the parent class. This is also not required, but in most cases will be done.
class Person{
constructor(name){
this.name = name;
}
sayHi(){
alert(`${this.name} says hello!`);
}
}
class SophisticatedPerson extends Person{
constructor(name){
super(name);//this will call the constructor method of the Person class
}
sayHi(){
alert(`${this.name} says: Top of the hat to you, sir!`);
}
oldSayHi(){
super.sayHi();//call sayHi of the Person class
}
}
let person = new SophisticatedPerson('Jerry');
person.sayHi();//calls the overidden sayHi method of SophisticatedPerson class
person.oldSayHi();//calls the oldSayHi of SophisticatedPerson class which calls sayHi of Person class
With 'super' you can call the constructor via 'super()' or any other methods via 'super.methodName()' of the parent class as illustrated above.
Extra notice: if you don't provide a constructor method on your child class, the parent constructor method will be called.
The constructor method is the method that's invoked when you construct an object with the new keyword. It's used to initialize fundamental properties of your object.
The reason you must call the parent constructor (other than the simple fact that this is how "the language rules" are defined) is that you need to allow the parent class to do its initialization.
These are fairly basic, common concepts in many OOP languages.

Categories

Resources