JavaScript Hoisting

Mar 23, 2022javascripthoisting

In JavaScript, hoisting refers to the default behavior of moving declarations of functions, variables or classes to the top of their scope, prior to execution of the code, so they too can be referenced before they are declared.

JavaScript only hoists declarations, not initializations! This means that initialization doesn’t happen until the associated line of code is executed, even if the variable was originally initialized then declared, or declared and initialized in the same line.

Note that it is the order in which code is executed that matters, not the order in which it is written in the source file. The code will succeed provided the line that initializes the variable is executed before any line that reads it.

Variables

Variables using var keyword, which declares a variable globally or locally to an entire function regardless of block scope, will be hoisted and has default value undefined.

It’s important to point out that only a variable’s declaration is hoisted, not its initialization. The initialization happens only when the assignment statement is reached. Until then the variable remains undefined (but declared):

console.log(x) // undefined (not ReferenceError)
var x = 1

Both let and const allow you to declare variables that are limited to the scope of a block statement, or expression on which it is used. Variables are hoisted but don’t have default value, throws ReferenceError when accessed before initialization.

The variable is said to be in a Temporal Dead Zone (TDZ) from the start of the block until the declaration has completed.

The term “temporal” is used because the zone depends on the order of execution (time) rather than the order in which the code is written (position). For example, the code below works because, even though the function that uses the let variable appears before the variable is declared, the function is called outside the TDZ.

{
  // TDZ starts at beginning of scope
  const func = () => console.log(letVar) // OK

  // Within the TDZ letVar access throws `ReferenceError`

  let letVar = 3 // End of TDZ (for letVar)
  func() // Called outside TDZ!
}

Functions

Function declarations in JavaScript are hoisted to the top of the enclosing function or global scope. You can use the function before you declared it:

console.log(calcRectArea(5, 6)) // OK

function calcRectArea(width, height) {
  return width * height
}

Note that function expressions are not hoisted:

notHoisted() // TypeError

var notHoisted = function () {
  console.log('bar')
}

Classes

An important difference between function declarations and class declarations is that while functions can be called in code that appears before they are defined, classes must be defined before they can be constructed.

Classes defined using a class declaration are hoisted, which means that JavaScript has a reference to the class. However the class is not initialized by default, so any code that uses it before the line in which it is initialized is executed will throw a ReferenceError.

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

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