How does hoisting affect function declarations and expressions?
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 declarationconsole.log(foo()); // Works finefunction foo() {return 'Hello';}// Function expressionconsole.log(bar()); // Throws TypeError: bar is not a functionvar 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 finefunction 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 functionvar 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 withundefinedduring scope setup.letandconst: The bindings are created but remain uninitialized in the TDZ, leading to aReferenceErrorif accessed before initialization.
console.log(baz); // undefinedvar baz = function () {return 'Hello';};console.log(qux); // ReferenceError: Cannot access 'qux' before initializationlet qux = function () {return 'Hello';};console.log(quux); // ReferenceError: Cannot access 'quux' before initializationconst quux = function () {return 'Hello';};
Further reading
- MDN Web Docs on Hoisting
- JavaScript.info on Hoisting
- MDN Web Docs on Function Declarations
- MDN Web Docs on Function Expressions