DEV Community

mmvergara
mmvergara

Posted on

2 2 1 1 3

Javascript Hoisting - What Moves and What Stays

Think of hoisting like setting up a stage before a play. Before the code runs (during the "creation phase"), JavaScript moves all declarations to the top of their scope like stagehands moving props into place before the curtain rises. The only catch is that the declarations are moved, not the initializations.

According to the ECMAScript specification, this behavior is actually part of how JavaScript creates what's called the "lexical environment" during the creation phase. But let's not get too technical just remember that JavaScript does a "pre-scan" of your code before running it.

var Hoisting

console.log(x); // Outputs: undefined
var x = 5;
console.log(x); // Outputs: 5
Enter fullscreen mode Exit fullscreen mode

In this example, the var declaration var x is hoisted to the top of the scope, but the initialization x = 5 is not. This is why the first console.log(x) outputs undefined instead of throwing an error. The variable x exists, but it hasn't been assigned a value yet.

let and const in the Temporal Dead Zone (TDZ)

console.log(y); // Throws a ReferenceError: Cannot access 'y' before initialization
let y = 10;
console.log(y); // Outputs: 10
Enter fullscreen mode Exit fullscreen mode

In this example, the let declaration let y is hoisted, but it is placed in the Temporal Dead Zone (TDZ) until the line where it is initialized. Trying to access y before its initialization results in a ReferenceError. This behavior is different from var, which would simply return undefined in a similar situation.

this also includes anything that is declared using let or const including functions ofcourse

The TDZ (Temporal Dead Zone)

Speaking of the temporal dead zone, this is where let and const declarations live before they're initialized. Unlike var which returns undefined when accessed before initialization, these modern declarations will throw an error. The ECMAScript spec calls this behavior "temporal dead zone semantics", but I just call it "JavaScript keeping us honest".

A slightly harder example

function setupEventHandler() {
  handleClick(); // Works!

  const config = {
    debug: true,
  };

  function handleClick() {
    if (config?.debug) {
      // Undefined!
      console.log("Debug mode");
    }
  }
}

setupEventHandler();
Enter fullscreen mode Exit fullscreen mode

See what happened there? The function declaration handleClick is hoisted, so we can call it early. But that object config? It stays right where it is. This is why accessing config inside handleClick gives us undefined, we're trying to read the script before its ready.

The Class Gotcha

Class hoisting doesn't work quite the same way.

const dog = new Animal(); // Throws an error!

class Animal {
  constructor() {
    this.type = "mammal";
  }
}
Enter fullscreen mode Exit fullscreen mode

While classes are hoisted, they stay in a "temporal dead zone" until their definition is evaluated. This means you can't access them before they're declared.


Just remember declarations are moved to the top, but initializations stay put.

Billboard image

Deploy and scale your apps on AWS and GCP with a world class developer experience

Coherence makes it easy to set up and maintain cloud infrastructure. Harness the extensibility, compliance and cost efficiency of the cloud.

Learn more

Top comments (0)

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay