Why Are Es6 Classes Not Hoisted

Why are ES6 classes not hoisted?

Why are ES6 classes not hoisted?

Actually they are hoisted (the variable binding is available in the whole scope) just like let and const are - they only are not initialised.

It would make sense to hoist its definition

No. It's never a good idea to use a class before its definition. Consider the example

var foo = new Bar(); // this appears to work
console.log(foo.x) // but doesn't

function Bar(x) {
this.x = x || Bar.defaultX;
}
Bar.defaultX = 0;

and compare it to

var foo = new Bar(); // ReferenceError
console.log(foo.x);

class Bar {
constructor (x = Bar.defaultX) {
this.x = x;
}
}
Bar.defaultX = 0;

which throws an error as you would expect. This is a problem for static properties, prototype mixins, decorators and everything. Also it is quite important for subclassing, which broke entirely in ES5 when you used a class with its non-adjusted prototype, but now throws an error if an extended class is not yet initialised.

Why are javascript functions within classes not hoisted?

class A {  f1() {    return f2()  }  f2() {    return 'f2'  }}
var a = new A()
console.log(a.f1())

How to get hoisting to work for extended classes?

In the related link is an example with the Hero class. There are many answer here in stackoverflow, that are telling, that classes are hoisted and that this behaviour changed between ES5 and ES6. Take a look at this answer: Why are ES6 classes not hoisted?

Yes but no. It is correct that the class name is hoisted. However, that it rather irrelevant to your situation.

Javascript is executed in two steps:

  1. Parsing/compilation
  2. Runtime

In the parsing step, the Javascript engine takes your code apart, reads it, tokenises it, and generally converts it into an executable form. In this step, it lays out general structures it's going to use during runtime, among them the names of things and what scope they belong to. So, the name MyActionEdit will be created in the correct scope. That is what hoisting is. Because in the second step, the runtime, when the code actually runs, that name will already exist, even if it comes later in the scope:

console.log(foo);

var foo = 'bar';

What is the correct placement of constructors or classes in JS code?

A variable is just a means to store and reference a value. An object is a type of value. A function is a type of object. A constructor function is a function designed to be called with the new keyword (which creates an object and sets up the prototype chain on it).

To call a constructor function, you need to have access to that value. This can be via a variable (and usually is).

The variable must contain the constructor function before you can reference it.


A function declaration is a means to create a function, which can be a constructor function, which is hoisted, allowing it to be used earlier in the code then it appears.


However, constructor functions typically have a number of methods added to the prototype and these are not hoisted so in the example below:

  1. The instance of dog is successfully constructed
  2. The attempt to bark fails because the assignment to prototype.bark hasn't happened yet

var fido = new Dog("Fido");fido.bark();
function Dog(name) { this.name = name;}
Dog.protype.bark = function () { alert(`Woof! I'm ${this.name}`);}

Typescript class: will class be hoisted when I use two classes?

The reason is that this line:

hero = new Hero('foo')

Is only evaluated when you instantiate AppComponent which is after the class Hero has been evaluated.

However, in your 2nd code snippet this line:

const b = new Bolygon();

Is evaluated first, before the class itself has been evaluated.

If you make AppComponent.hero static it will cause an error:

class AppComponent {
static hero = new Hero('foo') // errro: Hero is not a constructor
}

Bundling ES6 classes with Webpack. Is there a way to hoist extended classes?

Webpack does not simply put all the imports together into one file, but it keeps them as modules. Every module/file needs to import everything it depends on, so it would work if you just run that module in Node (after transpiling if necessary). Your a.js does not work by itself because classD is not defined. The correct way is to import it in a.js:

import ClassD from './d';

export default class ClassA extends ClassD {
constructor(...) {
super(...);
}
}

import x from './file' imports the default export as x, so you want to use export default for your classes.

You might be worried that importing a file from multiple places will include it multiple times, but that is not the case, it's included once and every import of that module will refer to the same one in the bundle.

Note: The convention in JavaScript is to use PascalCase for classes.

Are ES6 classes just syntactic sugar for the prototypal pattern in Javascript?

Yes, perhaps, but some of the syntactic sugar has teeth.

Declaring a class creates a function object that is the constructor for the class, using the code provided for constructor within the class body, and for named classes, with the same name as the class.

The class constructor function has a normal prototype object from which class instances inherit properties in normal JavaScript fashion. Instance methods defined within the class body are added to this prototype.

ES6 does not provide a means to declare class instance default property values (i.e. values which are not methods) within the class body to be stored on the prototype and inherited. To initialize instance value you can either set them as local, non inherited properties within the constructor, or manually add them to the class constructor's prototype object outside the class definition in the same fashion as for ordinary constructor functions. (I am not arguing the merits or otherwise of setting up inherited properties for JavaScript classes).

Static methods declared within the class body are added as properties of the class constructor function. Avoid using static class method names that compete with standard function properties and methods inherited from Function.prototype such as call, apply or length.

Less sugary is that class declarations and methods are always executed in strict mode, and a feature that gets little attention: the .prototype property of class constructor functions is read only: you can't set it to some other object you've created for some special purpose.

Some interesting stuff happens when you extend a class:

  • the prototype object property of the extended class constructor is automatically prototyped on the prototype object of the class being extended. This is not particularly new and the effect can be duplicated using Object.create.

  • the extended class constructor function (object) is automatically prototyped on the constructor function of the class being extended, not Function. While it may be possible to replicate the effect on an ordinary constructor function using Object.setPrototypeOf or even childClass.__proto__ = parentClass, this would be an extremely unusual coding practice and is often advised against in JavaScript documentation.

There are other differences such as class objects not being hoisted in the manner of named functions declared using the function keyword.

I believe it could be naive to think that Class declarations and expressions will remain unaltered in all future versions of ECMA Script and it will be interesting to see if and when developments occur. Arguably it has become a fad to associate "syntactical sugar" with classes introduced in ES6 (ECMA-262 standard version 6) but personally I try to avoid repeating it.



Related Topics



Leave a reply



Submit