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

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.

Related

does constructor allow to bind undefined method?

I am studying react and following the guide: event handling section, and watching binding of an event handler to a class in a constructor.
There's a thing I want to understand.
Why is it's okay to bind yet undefined method of a class in the constructor? or are methods of a class initialized before the constructor? or algoritm of class intialization starts with the constructor but it's possible to define already binded function later?
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
...
Function definitions in classes are initialized before the constructor runs. Here's an example:
class Bar {
constructor(props) {
console.log('Bar constructor');
console.log(props);
}
}
class Foo extends Bar {
constructor(props) {
super(props);
console.log('Foo constructor');
console.log(this.handleClick);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this);
}
}
const foo = new Foo({a: 1});
console.log('`this` in handleClick:');
foo.handleClick();
.as-console-wrapper { max-height: 100% !important; }
Why is it's okay to bind yet undefined method of a class in the constructor? or are methods of a class initialized before the constructor?
kind of, and not exactly. The point is JS' prototype based inheritance.
You create your class Foo {}. At that point the methods are defined on the class' prototype "long" before you even thought of calling new Foo(). When you then instantiate the class there are basically 3 things happening behind the scenes:
const instance = Object.create(null);
JS allocates the memory and creates a new (basically empty) object.
Object.setPrototypeOf(instance, Foo.prototype);
Then it attaches the prototype from the class and turns this object into an instance of the class.
But unlike other languages it does not actually write the methods onto the object, they are only available through the prototype chain. *that's why I wrote "not exactly"
Foo.call(instance, ...args);
Calling the constructor on the instance. This code-snippet is technically not correct, but representative.
That's why the method is already defined in the constructor. Through the prototype chain, the instance can access the method defined on the class' prototype object.
When you now do this.method = this.method.bind(this); you access the Foo.prototype.method, because the instance has no own property with that name, then you create a new function that is bound to this (the instance) and write that new bound function onto the instance.
And from that point on this.method will access the bound function on the instance and no longer the "original" method from the prototype.

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

ES6 Accessing child class's `this` when overriding a function in 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.

In JavaScript Classes, can a child call its super parent methods by default?

With a simple Child class that extends a Parent class and both of them define the same method:
class Parent {
method() {
console.log('Parent method');
}
}
class Child extends Parent {
method() {
// super.method()
console.log('Child method');
}
}
const child = new Child();
child.method();
Can the Child not override the method by default and instead have both methods fired (without needing to manually invoke the super method?)
No, you must invoke the super method. There is no OO language that I know of that allows a super method to be automatically invoked along with the derived version. If you think about it, it is the correct thing to do as sometimes you would want the super's version as well, and sometimes you would not.

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.

Categories

Resources