The concept of hoisting in JavaScript

What is hoisting?

Hoisting is a behavior where JavaScript acts as if variable and function declarations are “lifted” to the top of their scope before the code runs.

Hoisting of variables declared using var

var declarations are hoisted with an initial value of undefined; initialization remains in place.

console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1

Hoisting of variables declared using letconst, and class

let, const, and class declarations are hoisted but not initialized, causing a ReferenceError if accessed before their declaration—this is due to the Temporal Dead Zone (TDZ).

y; // ReferenceError: Cannot access 'y' before initialization
let y = 'local';

z; // ReferenceError: Cannot access 'z' before initialization
const z = 'local';

Foo; // ReferenceError: Cannot access 'Foo' before initialization

class Foo {
  constructor() {}
}

Hoisting of function expressions and arrow functions

Function declarations (function foo() {}) are fully hoisted including their body, so you can call them before their definition.

console.log(foo); // undefined
foo(); // Uncaught TypeError: foo is not a function

var foo = function () {
  console.log('FOO');
};

console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function

var bar = () => {
  console.log('BAR');
};

Hoisting of function declarations

Function expressions and arrow functions only hoist the variable name, not the assignment—calling them before initialization causes errors.

console.log(foo); // [Function: foo]
foo(); // 'FOO'

function foo() {
  console.log('FOO');
}

Hoisting of import statements

import declarations are hoisted too, and their side effects are executed before the rest of the module runs.

foo.doSomething(); // Works normally.

import foo from './modules/foo';

What are the potential issues caused by hoisting?

Hoisting can cause JavaScript code to behave unexpectedly because variable and function declarations appear moved to the top, which can lead to errors or surprising values.

var becomes undefined if used too early

Only the declaration is hoisted, not the initialization.

console.log(a); // undefined  
var a = 5;

Temporal Dead Zone (TDZ) with let and const

Accessing before declaration causes ReferenceError.

console.log(b); // ReferenceError: Cannot access 'b' before initialization  
let b = 10;

Function declarations vs expressions

Function declarations are fully hoisted; function expressions via var are not—calling them early throws TypeError.

foo(); // works
function foo() { console.log('Hi'); }

bar(); // TypeError: bar is not a function
var bar = function() { console.log('Hi'); };

Unintentional redeclaration with var

var does not have block scope; can override variables unexpectedly.

var x = 1;
if (true) { var x = 2; }
console.log(x); // 2

To avoid hoisting-related confusion, just declare variables and functions before using them—it makes code more predictable.

Use let and const instead of var

They offer block scoping and do not auto-initialize like var, so they reduce hoisting surprises.

console.log(x); // undefined. (Hoisted but uninitialized, risk of unexpected behavior)
console.log(y); // ReferenceError: Cannot access 'y' before initialization. (Not hoisted)
console.log(z); // ReferenceError: Cannot access 'z' before initialization. (Not hoisted)

// Avoid using var
var x = 10; // Hoisted to the top of the function or global scope

// Use let or const
let y = 20; // Block-scoped, not hoisted to the top
const z = 30; // Block-scoped, not hoisted to the top

Declare variables at the top of their scope

Placing declarations at the start of functions or blocks improves readability and prevents undefined issues.

function example() {
  let a = 1;
  const b = 2;

  // Now use a and b
  console.log(a + b);
}
example(); // Output: 3

Declare functions before calling them

Avoid accidental calls to functions not yet defined due to hoisting.

// Function declaration (hoisted)
function myFunction() {
  console.log('Hello, world!');
}
myFunction(); // No issues here

// Function expression (not hoisted)
const anotherFunction = function () {
  console.log('Hello again!');
};
anotherFunction(); // No issues here

Avoid undeclared variables

Use "use strict"; to enforce declaration and prevent unintentional globals.

"use strict";

// Avoid this
function badExample() {
  x = 10; // ReferenceError in strict mode
  console.log(x);
}

// Do this instead
function goodExample() {
  let x = 10; // x is declared
  console.log(x);
}

goodExample();
badExample();