How ES6 classes compiled? - javascript

While I was looking to react native animations documentation, I read something interesting to me. Code piece:
class FadeInView extends React.Component {
state = {
fadeAnim: new Animated.Value(0), // Initial value for opacity: 0
}
// Other parts of code piece ...
// https://facebook.github.io/react-native/docs/animations.html
}
Let's break down what's happening here. In the FadeInView constructor, a new Animated.Value called fadeAnim is initialized as part of state.
In the above statement docs mentioning that state is initialized in constructor. However there is no constructor in this code piece. From the knowledge of other languages like c# and c++ etc. variables can be declared and initialized in classes without constructor.
I actually didn't understand underlying process in javascript how these variables declared and initialized in es6 classes.
Why he is mentioning constructor while there isn't any constructor.
How es6 classes compiled? (This might be long, overall picture of that can be enough for my needs)

The state is a class property, currently not an ES6+ standard feature.
Therefore you need the Babel Stage 2 preset in order to transform class properties.
Currently, that plugin transformation moves the class properties into the constructor. Check the babel repl output
Simplified Babel output:
function FadeInView() {
this.state = {
fadeAnim: new Animated.Value(0) // Initial value for opacity: 0
};
};
More info related to class field declarations

Class properties (a js proposal yet) are actually moved into the constructor:
class FadeInView extends React.Component {
constructor() {
this.state = {
fadeAnim: new Animated.Value(0), // Initial value for
opacity : 0
};
}
}

Related

Not able to set properties in JavaScript class

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.

ES6 class inheritance without "extends" keyword

I'd like to do inheritance in an es6 class without the extends keyword:
Typical approach:
class Foo extends Bar {
contructor() {
...
}
}
What I am looking for is to generate an object with the same signature but using this pattern:
class Foo {
contructor(Bar) {
// use Bar class somehow
...
}
}
Thanks
== EDITS ==
Context:
I build an extension (ami) for a JS library threejs.
It provides new objects that seamlessly work in threejs.
Problem:
threejs has an internal mechanism to generate unique ids for each object, that is critical for its proper behavior.
Current implementations rely on three to be exposed as a global variable, so anybody that creates an object must reference it to ensure the ids are actually unique.
// in ami
// ID of this object will be unique accros all classes
// that are based of global THREE.Mesh
class Foo extends THREE.Mesh {
contructor() {
...
}
}
Using global variable works fine but I want to get rid of the global namespace requirement.
If I do not reference the same base elements in ami and in my application, id can conflict.
// in ami
import {Mesh} from 'three';
class Foo extends Mesh {
contructor() {
...
}
}
// in my app
import {Foo} from 'ami';
imoport {Mesh} from 'three';
const foo = new Foo(); // it uses "Mesh" from ami as a base.
const mesh = new Mesh(); // it uses current "Mesh" as a base.
// IDs will conflict...
One solution that could work is that I provide a new argument in ami constructors, to provide the three reference:
// in ami
class Foo {
contructor(mesh) {
...
}
}
// in my app
imoport {Mesh} from 'three';
import {Foo} from 'ami';
const foo = new Foo(Mesh);
const mesh = new Mesh();
But I do not know how to implement this solution.
This answer addresses the question as originally posed and was good enough to help the OP refine the question and garner immediate upvotes. I'd appreciate it if you didn't downvote it merely because the goalposts have moved.
Assuming that you are not crazy and this is a learning exercise, the best way to learn how to implement this is to get Typescript, write a class using extends, compile it with ES5 as the target, and look at the generated JavaScript. Ensure your base class has methods, properties, static methods, static properties, and a constructor with mixed required and optional parameters. Then derive another class from it and override some methods and replace some. You'll see how it's done by people who got serious about it.

When can we avoid using a constructor to initialize state?

In the example given on the reactnativeexpress website here, state = {count: 0} is used in place of a constructor to initialize the count variable.
In the official Facebook docs here, they've said
In general, you should initialize state in the constructor, and then call setState when you want to change it.
So I created a constructor to initialize count in the same manner:
constructor(props) {
super(props);
this.state = {count: 0}
}
The code seems to work the same way (at least on the surface), so what's the difference between creating a constructor and initializing count as in the example?
In the example that you attach as a link they use the ES Class Fields which is a proposal (currently in stage 2).
That means you will need some extra presets / plugins of babel (or any other JavaScript compiler) to support it as it's not part of the official specifications of ECMAScript yet.
In the other example, you use the class constructor which is part of ES2015.
Both will get you the same results, a property attached to the instance of the class.
which is basically just a syntactic sugar for the prototype pattern.
This has more to do with ES6 Classes.
getFullName() {
return this.props.firstName + this.props.lastName;
}
The above function will throw an error saying props is not defined.
getFullName = () => {
return this.props.firstName + this.props.lastName;
}
The above code will pass the context of this into the function and will work good.
Similarly, your
state = {count: 0}
is executed in the context of the class [using appropriate this ].
The same state needs to be called using this.state within the constructor to bind it to appropriate context.

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.

ES6 class store [duplicate]

This question already has an answer here:
ES6 classes : what about instrospection?
(1 answer)
Closed 6 years ago.
I just can't find out where the references to declared ES6 classes are stored, I would have expected them in the window Object, bit they don't appear there.
I don't think it's a duplicate of ES6 classes : what about instrospection? since he is asking for a existance check of a class, what I want is a list of available classes.
For example:
class Test {
constructor() {
}
}
window.Test // undefined
What I want is a list of all classes that extend a class of mine
To clarify that I have a structure that looks something like this:
class Base {
constructor(components) {
for(let component of components) {
window[component](); // window.Test2 not defined
}
}
start() {
new this();
}
}
class Test2 extends Base {
constructor() {
super();
}
}
class Test extends Base {
constructor() {
super(['Test2','Test2']);
}
}
Test.start();
That's just an abstraction of my structure, in short I have to use strings at super(['Test2', 'Test2'])
At the moment I'm doing something like this
Base.register(Test2);
for every class and I want to get rid of that.
You can use Class expressions to store them in some sort of array, although I probably wouldn't do it, if I were you.
var Test = class Test {
constructor() {
}
}
allClasses.push(Test);
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.
Basically ES6 classes are compiled to plain old-fashioned Javascript functions. You may "store" them at the window object but this is a major pitfall as you are killing the whole module patter ES6 introduced.
If you want sort of a "module" of classes, you could theoretically do something like this:
// some-class.js
export default class SomeClass {}
Then:
// another-class.js
export default class AnotherClass {}
And your entry file:
// index.js
import SomeClass from './some-class.js' // extensions optional; here just for clarity
import AnotherClass from './another-class.js'
export default {
SomeClass,
AnotherClass
}
If you have all of those embedded in the same directory (we'll call the directory example), you can just import that entire module wherever you need it:
// something-that-requires-your-module.js
// this will by default enter through the `index.js` file of your `example` directory
import Example from './example';
console.log(Example.SomeClass);
console.log(Example.AnotherClass);

Categories

Resources