Understanding JavaScript Hoisting: How Variables and Functions are Lifted

Understanding JavaScript Hoisting: How Variables and Functions are Lifted

Understanding JavaScript Hoisting: How Variables and Functions are Lifted

undefined undefined

Cutcu H.

Cutcu H.

October 12, 2024

Understanding JavaScript Hoisting: How Variables and Functions are Lifted
undefined undefined

Cutcu H.

Cutcu H.

October 12, 2024

JavaScript hoisting is one of those tricky concepts that often causes confusion among developers, especially those new to the language. Knowing how hoisting works can save you from many headaches when dealing with variable declarations and function invocations. In this article, we'll explore what hoisting is, how it works with different types of variables, and how it affects functions with sample code.

What Is Hoisting?
What Is Hoisting?

What Is Hoisting?

Hoisting is JavaScript's default behavior of moving variable and function declarations to the top of their containing scope (the global scope or the current function scope). This means that you can reference variables and functions before they are actually declared in your code.

How Hoisting Works with Variables
How Hoisting Works with Variables

How Hoisting Works with Variables

When you declare a variable with var, let, or const, the declaration is hoisted to the top of the scope. However, the behavior of initialization differs:

var: The variable is hoisted and initialized to undefined by default. This means the declaration is moved to the top of the scope, and its value is set to undefined until the line where it is explicitly assigned a value.
Example:

"index.js"

console.log(hoistedVar); // undefined
var hoistedVar = 'This variable is hoisted';
console.log(hoistedVar); // "This variable is hoisted"

Explanation: The var hoistedVar declaration is hoisted to the top of the scope, but the assignment ("This variable is hoisted") happens where it appears in the code. That's why the first console.log prints undefined.

let and const: The declaration is hoisted, but no initialization occurs. These variables are in a "temporal dead zone" until the actual line of initialization is encountered. Attempting to access them before initialization results in a ReferenceError.
Example:

console.log(hoistedLet); // ReferenceError: Cannot access 'hoistedLet' before initialization
let hoistedLet = 'This variable is hoisted';

Explanation: In this case, the let hoistedLet declaration is hoisted, but it is not initialized until the code reaches the assignment. Therefore, trying to access the variable before initialization throws a ReferenceError.

Hoisting with Functions
Hoisting with Functions

Hoisting with Functions

Unlike variables, function declarations are completely hoisted. This means you can call a function before it's defined in the code, and it will still work:

hoistedFunction(); // "This function is hoisted"

function hoistedFunction() {
  console.log('This function is hoisted');
}

Explanation: The entire function declaration (both the definition and the body) is hoisted to the top of the scope, so it can be invoked even before the line where it appears in the code.

However, function expressions (functions assigned to variables) are treated differently:

hoistedFunctionExpression(); // TypeError: hoistedFunctionExpression is not a function

var hoistedFunctionExpression = function () {
  console.log('This is a function expression');
};

Explanation: In this case, the variable hoistedFunctionExpression is hoisted, but its value (the function expression) is not assigned until runtime. Therefore, calling it before the assignment results in a TypeError because hoistedFunctionExpression is undefined at that point.

Summary of Hoisting Behavior
Summary of Hoisting Behavior

Summary of Hoisting Behavior

"Summary of Hoisting Behavior"

| Type                  | Hoisted?          | Initialization  | Accessible before declaration? |
| --------------------- | ----------------- | --------------- | ------------------------------ |
| var                   | Yes               | undefined       | Yes, but value is undefined    |
| let                   | Yes               | Not initialized | No (ReferenceError)            |
| const                 | Yes               | Not initialized | No (ReferenceError)            |
| Function Declarations | Yes (Entire body) | Fully hoisted   | Yes                            |
| Function Expressions  | Yes (Variable)    | No (Expression) | No (TypeError)                 |
Avoiding Confusion with Hoisting
Avoiding Confusion with Hoisting

Avoiding Confusion with Hoisting

To avoid issues caused by hoisting, consider the following best practices:

  1. Declare all variables at the top of their scope so their hoisting behavior doesn't surprise you.
  2. Use let or const instead of var to get clearer error messages if you access variables before initialization.
  3. If using function expressions, declare them before you call them.
Conclusion
Conclusion

Conclusion

Hoisting is one of those quirky JavaScript features that can trip you up if you're not aware of it. By understanding how hoisting works for variables and functions, you can write cleaner, more predictable code. Remember that while declarations are hoisted,
initializations are not. Function declarations are hoisted entirely, but function expressions are not. Stick to these concepts, and you'll be able to master hoisting in no time.