Not able to set properties in JavaScript class - javascript

I am simply setting properties in a class in JavaScript:
class BookingReports extends ReportsInterface{
var categoryID;
constructor() {
super();
}
init(CatID)
{
this.categoryID=CatID;
}
}
But JavaScript is not accepting variable at all and giving error "unexpected identifier".
I am not getting idea what is syntax error. Is it because of inheritance or super keyword? I even tried using binding with this whole declaration. But it is not working.

var categoryID is just out of place there. You don't declare variables at the class level; you declare variables within the constructor or methods.
In your case, you probably don't want to declare a variable at all; instead, create the property in your constructor:
class BookingReports extends ReportsInterface{
constructor() {
super();
this.categoryID = /*...some default value...*/;
}
init(CatID)
{
this.categoryID=CatID;
}
}
Side note: Having an init method doesn't make much sense; that's what the constructor is for. So:
class BookingReports extends ReportsInterface {
constructor(catID) {
super();
this.categoryID = catID;
}
}
At some point, the class fields proposal will reach Stage 4, at which point you will be able to "declare" properties at the class level if you want to:
// Valid if/when the class fields proposal advances
class BookingReports extends ReportsInterface {
categoryID; // Declares the property
constructor(catID) {
super();
this.categoryID = catID;
}
}
If you're initializing the property in the constructor, there's not a lot of point to that; but it can be useful for telling the reader (and JavaScript engine) the standard "shape" of your object up-front rather than making them (and it) analyze the constructor's code.

Related

How to access subclass from parent in ES6? [duplicate]

This question already has answers here:
Get parent class name from child with ES6?
(5 answers)
Call static methods from regular ES6 class methods
(3 answers)
Closed 5 years ago.
I have a base class that looks like this:
class Speaker {
sayHi() {
if (this.isLoud) {
console.log('HI');
} else {
console.log('hi');
}
}
}
And a subclass that looks like:
class LoudSpeaker extends Speaker {
}
LoudSpeaker.isLoud = true;
... where that last line is an attempt at creating a class variable in ES6. The problem is that when I instantiate LoudSpeaker and call sayHi, the isLoud variable is undefined. Is there anyway of accessing a class variable of a subclass from the parent class?
this.isLoud is not the same thing as LoudSpeaker.isLoud. The first refers to an instance variable, the second refers to a property of the LoudSpeaker constructor function which other languages would call a class variable or a static property. In Javascript, you do not refer to class variables using this.isLoud.
To override the value of the instance variable isLoud in a subclass, you would set its value in the constructor.
class Speaker {
constructor() {
this.isLoud = false; // set default value
}
sayHi() {
if (this.isLoud) {
console.log('HI');
} else {
console.log('hi');
}
}
}
class LoudSpeaker extends Speaker {
constructor() {
super();
this.isLoud = true; // override to set our own value
}
}
If you really want the isLoud property to be something that a class sets once and is never set again, then you can set it on the prototype and then you can reference it with this.isLoud from within an instance:
class Speaker {
sayHi() {
if (this.isLoud) {
console.log('HI');
} else {
console.log('hi');
}
}
}
Speak.prototype.isLoud = false; // set default value
class LoudSpeaker extends Speaker {
}
LoudSpeaker.prototype.isLoud = true; // override for this class
This is usually not done in Javascript because if you set this.isLoud = whatever, then it sets an "own" property and the property becomes an instance property that is specific to that instance and that can be really confusing and bug causing. So, this is generally not done. If, however, you initialize the value on the prototype, never set it on the instance so you only read its value with x = this.isLoud, then it does behave as a class variable that is accessible via this.
There is discussion of this topic in the ES6 wiki. See ES6 class variable alternatives for details as this capability is intentionally left out of the ES6 class syntax which is why it's implemented here as either a regular instance variable set in the constructor or a property set directly on the prototype (and not via the class syntax).

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.

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.

ES6, access props set by the child from the parent's constructor

I am trying to set up a class hierarchy with ES6 classes.
All entities will inherit from single Base class. It will need to access properties exposed by the Child class in a generic manner. Like this:
class Base {
constructor() {
this.doSomethingWithProps();
}
doSomethingWithProps() {
console.log(Object.keys(this));
}
}
class Entity extends Base {
constructor() {
super();
this.key = "value";
}
}
Obviously, in the example above, Base class will not see the key prop set up by Entity. Ideally I would move the this assignment before super(), but that's not allowed. It would also be nice to be able to set properties before constructor, but AFAIK, that's not possible either.
The only solution I am left with is doing something like the following in each Entity:
class Base {
doSomethingWithProps() {
console.log(Object.keys(this));
}
}
class Entity extends Base {
constructor() {
super();
this.key = "value";
this.doSomethingWithProps();
}
}
However, besides being less than ideal, it will also create problems if I then want to inherit from Entity. doSomethingWithProps would then need to be able to detect if it's the "top-most" method in call hierarchy and only do its thing then. The only way to achieve that (that I can think of) would involve even more boilerplate.
Is there some solution I'm missing here? I'd be open to using a different OOP pattern if needed, although I'd like to stay as close as possible to native ES6 classes.
What you are trying to do is quite impossible. The parent initialisation always runs before the child initialisation, so it is imperative that the parent constructor does not rely on properties that might be overwritten by the child. This is a language-agnostic problem, btw.
The solution is to use parameters for the constructor, which can be modified in the child before they reach the parent code:
class Base {
constructor(key) {
this.key = key;
// do something with key
}
}
class Entity extends Base {
constructor() {
super("value");
}
}
console.log(new Entity);
or more generic
class Base {
constructor(props) {
this.doSomething(props);
Object.assign(this, props);
}
doSomething(props) {
return Object.keys(props);
}
}
class Entity extends Base {
constructor() {
super({key: "value"});
}
}
console.log(new Entity);
Also notice that constructors should always be pure. Their only purpose is to initialise the new instance from the arguments, and do nothing else like execute some side effects. A constructor should usually not need to call any (overwritable) instance methods (static methods are ok though).
So if you need to do something like that nonetheless when creating your instances, just don't do it in the constructor. Instead, call a method after using the constructor:
class Base {
log() {
console.log(Object.keys(this));
return this;
}
}
class Entity extends Base {
constructor() {
super();
this.key = "value";
}
}
var e = new Entity().log();
You can also abstract that out:
class Base {
static createAndLog(...args) {
var x = new this(...args);
x.log();
return x;
}
…
}
…
var e = Entity.createAndLog();
Depending on how complex you want to go, there's probably two routes you could go down.
1. Keep state in Base
If possible, pass the props you need from Entity into Base and maintain state there.
class Base {
constructor(props) {
for (var k in props) {
this[k] = props[k]
}
this.doSomethingWithProps();
}
doSomethingWithProps() {
console.log(Object.keys(this));
}
}
class Entity extends Base {
constructor(props) {
super(props);
}
}
let entity = new Entity({key: 'value'})
// ["key"]
2. Pass reference to Entity into Base
Note, I was incorrectly assuming separate instances of Entity.. this is overly redundant.
Not as simple, and will technically contain a self referencing key as Entity will inherit the child key from Base. This would definitely be considered a "wrong" approach, but I'm including it for the heck of it.
class Base {
constructor() {
this.child
}
registerChild(child) {
this.child = child
this.doSomethingWithProps()
}
doSomethingWithProps() {
console.log(Object.keys(this.child));
}
}
class Entity extends Base {
constructor() {
super();
this.key = 'value'
this.registerChild(this)
}
}
let entity = new Entity()
// ["key", "child"]
I think the observation here should be: avoid calling a method that relies on the object's state in the constructor, since constructors are meant to build that state.
Actually, what you suppose to ask is about js syntax, however, the root of the issue is the idea of oop
Consider what extends mean when you write:
Entity extends Base
It means Entity make use of something from Base, the keyword is extends rather than something like communicate, which means Base should NOT care about Entity at the first place. extends can also mean Entity has something which is not expected by Base, thus extends.
However, from my own point of view(correct me if I am wrong), there are some valid use cases in which parent class has some awareness of child class.
For example: put some common logic in Parent class to manipulate the property of Child, in such case, from the point of view of logic(not syntax), the child property should be static, i.e. belong to the whole class rather than a instance

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