Key Takeaways
- Hoisting in JavaScript refers to the interpreter’s action of moving all variable and function declarations to the top of the current scope, allowing you to use variables and functions before they are declared in the code. However, only the declarations are hoisted, not the assignments.
- Function declarations are hoisted in JavaScript, allowing the function to be called before it is declared in the code. However, function expressions, including those involving arrow functions, are not hoisted, leading to an error if called before their definition.
- Variables declared with ‘var’, ‘let’, and ‘const’ are all hoisted in JavaScript, but they behave differently. ‘Var’ is initialized with ‘undefined’ when hoisted, allowing its use before declaration. ‘Let’ and ‘const’, while hoisted, are not initialized, resulting in a Reference Error if accessed before declaration. This period is known as the Temporal Dead Zone.
(function() {
var foo = 1;
var bar = 2;
var baz = 3;
alert(foo + " " + bar + " " + baz);
})();
This looks like sane JavaScript code. As expected, it displays the string "1 2 3"
. Now, assume that the alert is moved, as shown below.
(function() {
var foo = 1;
alert(foo + " " + bar + " " + baz);
var bar = 2;
var baz = 3;
})();
If someone actually wrote this code, it was probably by mistake. Clearly, the alert takes place before bar
and baz
are declared. However, this is perfectly valid JavaScript, which does not generate an exception. Instead, the alert displays "1 undefined undefined"
.
Based on our previous experiment, it seems that you can reference variables that don’t exist yet. Now, let’s take the same IIFE, but remove the baz
declaration altogether, as shown below. Suddenly, we have a ReferenceError
because baz
is not defined.
(function() {
var foo = 1;
alert(foo + " " + bar + " " + baz);
var bar = 2;
})();
This is truly interesting behavior. To understand what’s going on here, you have to understand hoisting. Hoisting is the JavaScript interpreter’s action of moving all variable and function declarations to the top of the current scope. However, only the actual declarations are hoisted. Any assignments are left where they are. Therefore, our second example IIFE actually translates to the following code.
(function() {
var foo;
var bar;
var baz;
foo = 1;
alert(foo + " " + bar + " " + baz);
bar = 2;
baz = 3;
})();
Now it makes sense why the second example didn’t generate an exception. After hoisting, bar
and baz
are actually declared before the alert statement, albeit with undefined values. In the third example, baz
was removed completely. Therefore, there was nothing to hoist, and the alert statement resulted in an exception.
Function Hoisting
As previously mentioned, function declarations are also hoisted. However, functions that are assigned to variables are not hoisted. For example, the following code will work as expected due to function declaration hoisting.foo();
function foo() {
alert("Hello!");
}
However, the following example will fail spectacularly. The variable declaration for foo
is hoisted before the function call. However, since the assignment to foo
is not hoisted, an exception is thrown for trying to call a non-function variable.
foo();
var foo = function() {
alert("Hello!");
};
Conclusion
Hoisting is an easy to understand, but often overlooked nuance of the JavaScript language. Without a proper understanding of hoisting, your programs are susceptible to subtle bugs. To help avoid these bugs, many developers (and linting tools) advocate for a single variable declaration statement at the very beginning of every scope. Since this is how the JavaScript interpreter essentially sees your code, there is validity to this rule – even if I am personally guilty of breaking it.Frequently Asked Questions about JavaScript Hoisting
What is the difference between var, let, and const in terms of hoisting in JavaScript?
In JavaScript, all three, var, let, and const, are hoisted. However, they behave differently. When ‘var’ is hoisted, it is initialized with ‘undefined’. This means you can use a variable declared with ‘var’ before its declaration. On the other hand, ‘let’ and ‘const’ are also hoisted but are not initialized. Therefore, if you try to use a ‘let’ or ‘const’ variable before its declaration, you will get a Reference Error. This area between the start of the scope until the declaration is known as the Temporal Dead Zone.
Can functions be hoisted in JavaScript?
Yes, functions can be hoisted in JavaScript. Function declarations are hoisted completely to the top of their scope. This means that the function can be called before it is declared in the code. However, function expressions, including those involving arrow functions, are not hoisted. If you try to call a function expression before its definition, you will get an error.
What is the Temporal Dead Zone in JavaScript hoisting?
The Temporal Dead Zone (TDZ) is a behavior in JavaScript related to ‘let’ and ‘const’ variables. It’s the period from the start of the scope until the declaration is processed, during which these variables cannot be accessed. If you try to access them, JavaScript will throw a Reference Error. The TDZ ends after the line of code where the variable is declared.
How does hoisting affect the order of code execution in JavaScript?
Hoisting can significantly impact the order of code execution in JavaScript. Since variable and function declarations are moved to the top of their containing scope during the compile phase, it allows you to use functions and variables before they are declared in the code. However, it’s important to note that only the declarations are hoisted, not the initializations.
Is it possible to avoid hoisting in JavaScript?
Hoisting is a default behavior in JavaScript and cannot be turned off. However, you can manage it effectively by always declaring variables and functions at the top of your scope. This way, you can ensure that hoisting does not lead to unexpected results in your code.
How does hoisting work with classes in JavaScript?
Class declarations, like function declarations, are hoisted. However, they are not initialized with ‘undefined’. Therefore, if you try to instantiate a class before its declaration, JavaScript will throw a Reference Error. This is similar to the behavior of ‘let’ and ‘const’ variables.
How does hoisting work in ES6 and later versions?
In ES6 and later versions, ‘let’ and ‘const’ were introduced, which have different hoisting behavior compared to ‘var’. While ‘var’ variables are hoisted and automatically initialized as ‘undefined’, ‘let’ and ‘const’ variables are hoisted but remain uninitialized. This means that a Reference Error will be thrown if they are accessed before their declaration.
What is the impact of hoisting on JavaScript performance?
Hoisting does not have a significant impact on JavaScript performance. The JavaScript engine processes the hoisting during the compile phase, not the execution phase. Therefore, it does not affect the runtime performance of the JavaScript code.
Can hoisting lead to bugs or errors in JavaScript code?
Yes, hoisting can potentially lead to bugs or errors in your JavaScript code. This is especially true if you try to use a variable before it’s declared and initialized. To avoid such issues, it’s a good practice to always declare and initialize your variables at the top of their scope.
How does hoisting work with JavaScript modules?
In JavaScript modules, ‘var’, ‘let’, ‘const’, and function declarations are hoisted to the top of the module, not to the global scope. This is because each module in JavaScript has its own scope. However, they follow the same hoisting rules as in the global or function scope.
Colin Ihrig is a software engineer working primarily with Node.js. Colin is the author of Pro Node.js for Developers, and co-author of Full Stack JavaScript Development with MEAN. Colin is a member of the Node.js Technical Steering Committee, and a hapi core team member. Colin received his Bachelor of Science in Engineering, and Master of Science in Computer Engineering from the University of Pittsburgh in 2005 and 2008, respectively.