Why are ES6 classes not hoisted?

JavascriptEcmascript 6

Javascript Problem Overview


Since ES6 classes are just a syntactical sugar over JavaScript's existing prototype-based inheritance [1] it would (IMO) make sense to hoist it's definition:

var foo = new Foo(1, 2); //this works

function Foo(x, y) {
   this.x = x;
   this.y = y;
}

But the following won't work:

var foo = new Foo(1, 2); //ReferenceError

class Foo {
   constructor(x, y) {
      this.x = x;
      this.y = y;
   }
}

Why are ES6 classes not hoisted?

Javascript Solutions


Solution 1 - Javascript

> 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.

Solution 2 - Javascript

While non-hoisted classes (in the sense that they behave like let bindings) can be considered preferable as they lead to a safer usage (see Bergi's answer), the following explanation found on the 2ality blog seems to provide a slightly more fundamental reason for this implementation:

> The reason for this limitation [non-hoisting] is that classes can have an extends clause whose value is an arbitrary expression. That expression must be evaluated in the proper “location”, its evaluation can’t be hoisted.

Solution 3 - Javascript

In Javascript all declarations (var, let, const, function, function, class) are hoisted* but it should be declared in same scope.

As you told "ES6 classes are just a syntactical sugar over JavaScript's existing prototype-based inheritance"

So Let's understand what it is?

Here you declared a class which is in fact "special function".Let's assume that your function Foo() and class Foo both are in global scope.

class Foo {
   constructor(x, y) {
      this.x = x;
      this.y = y;
   }
}

Following is the compiled code of your class Foo.

var Foo = (function () {
    function Foo(x, y) {
        this.x = x;
        this.y = y;
    }
    return Foo;
}());

Internally your class is converted to function with the same name inside wrapper function(iife) and that wrapper function returns your function.

Because your function's(class) scope is changed. and you are trying to create object of function in global scope which is in reality not exist.

you get the function in variable Foo once compilation comes to that. so later you have function in var you can create object of that.

Solution 4 - Javascript

Classes are not hoisted because, for example when a class extends an expression rather than a function, error occurs:

 class Dog extends Animal {}
 var Animal = function Animal() {
 this.move = function () {
 alert(defaultMove);
 }
 }
var defaultMove = "moving";
var dog = new Dog();
dog.move();

After hoisting this will become:

var Animal, defaultMove, dog;
class Dog extends Animal {}
Animal = function Animal() {
this.move = function () {
alert(defaultMove);
}
}
defaultMove = "moving";
dog = new Dog();
dog.move();

Such at the point where class Dog extends Animal is interpreted Animal is actually undefined and we get an error. We can easily fix that by moving the Animal expression before the declaration of Dog. Pls see this great article about the topic: https://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPetr PellerView Question on Stackoverflow
Solution 1 - JavascriptBergiView Answer on Stackoverflow
Solution 2 - JavascriptOliver SiewekeView Answer on Stackoverflow
Solution 3 - Javascriptmihir hapaliyaView Answer on Stackoverflow
Solution 4 - JavascriptLiviu CorneaView Answer on Stackoverflow