Quiz

How does hoisting affect function declarations and expressions?

Topics
JavaScript

TL;DR

Function declarations are initialized when JavaScript instantiates their scope, so they can be called before their source declaration. Function expressions are created only when evaluation reaches the expression. Before then, a var binding contains undefined and calling it throws TypeError; a let or const binding is in the temporal dead zone and accessing it throws ReferenceError. "Hoisting" describes these binding rules; the engine does not move source text.

// Function declaration
console.log(foo()); // Works fine
function foo() {
return 'Hello';
}
// Function expression
console.log(bar()); // Throws TypeError: bar is not a function
var bar = function () {
return 'Hello';
};

Hoisting in JavaScript

Function declarations

Function declarations are created and initialized when their containing scope is instantiated. This means you can call the function before its declaration appears in source order.

console.log(foo()); // Works fine
function foo() {
return 'Hello';
}

In the example above, the binding for foo already contains the function before statement evaluation begins, so the earlier call works.

Function expressions

Function expressions, on the other hand, are evaluated where they appear. The variable binding may already exist, but it does not contain the function until the assignment or declaration initializer runs.

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

In this example, scope setup creates bar and initializes it to undefined, but the assignment function() { return 'Hello'; } runs only at its source location. Therefore, calling bar() before the assignment results in a TypeError.

Differences between var, let, and const

The hoisting behavior differs between var, let, and const when used with function expressions.

  • var: The binding is created and initialized with undefined during scope setup.
  • let and const: The bindings are created but remain uninitialized in the TDZ, leading to a ReferenceError if accessed before initialization.
console.log(baz); // undefined
var baz = function () {
return 'Hello';
};
console.log(qux); // ReferenceError: Cannot access 'qux' before initialization
let qux = function () {
return 'Hello';
};
console.log(quux); // ReferenceError: Cannot access 'quux' before initialization
const quux = function () {
return 'Hello';
};

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise 1 of 2
Check your understanding Exercise 1 of 2

What does this classic script log?

function inspect() {
console.log(declared());
console.log(typeof expressed);
function declared() {
return 'ready';
}
var expressed = function () {
return 'later';
};
}
inspect();