Hoisting in JavaScript
Understanding how hoisting works with var, let, const, and function declarations can help you avoid tricky bugs in JavaScript code.
Join the DZone community and get the full member experience.
Join For FreeIn JavaScript, hoisting is when variable or function declarations are lifted to the top of their respective scope before the execution. This implies that a variable or a function can be used before defining it.
Hoisting Variables With var
The behavior of hoisting can be observed with an example of a variable declared with the help of var
. Consider the following code example:
console.log(myVar); // Output: undefined
var myVar = 10;
console.log(myVar); // Output: 10
Slightly naive, one might think that the console.log(myVar)
before the declaration will throw an error because myVar
is not declared yet. However, due to hoisting, the declaration of myVar
is moved to the top of the scope, and the preceding code will run without any error. It behaves as if the code was written like this:
var myVar;
console.log (myVar); // Output: undefined
myVar = 10;
console.log (myVar); // Output: 10
Only the declaration var myVar
is hoisted, the assignment myVar = 10
is not. Therefore, when the variable is logged the first time it reads as undefined
because while the variable name has been declared, it has not been assigned a value.
The Problem With var
A problem associated with var
is that the scope of its variable is not limited to the block it’s declared on. This can cause certain behaviors that may not be desired when using loops or any other control structure; for instance, if
statements.
if (true) {
var testVar = "I am inside the block";
}
console.log (testVar); // Output: "I am inside the block"
Even though testVar
is declared inside the “if block”, it can be used outside. As noted above, var
lacks a block-level scope but, rather possesses a function-level or a global one.
Hoisting With let and const
To overcome the problems that come with var
, JavaScript included let
and const
in ES6. These keywords are also hoisted, but with a significant difference: actualizing is done when the variable is needed in the program, and only at the time their declaration statement is met in the program. Until then, the variables are in the Temporal Dead Zone (TDZ), and reading it will cause a Reference Error
.
Let’s look at an example:
console.log (myLet); // Reference Error: Cannot access 'myLet' before initialization
let myLet = 5;
In this case, let
is moved to the top of the block; however, it is only declared, but not assigned to any value until the line let myLet = 5
is under execution. This means that while the variable is in scope — i.e., it has been declared — it cannot be used before it's assigned a value.
The same behavior applies to const
:
console.log (myConst); // Reference Error: Cannot access 'myConst' before initialization
const myConst = 10;
Here, the const
variable is declared but not defined, so if the script tries to use it before it is defined, it will throw a Reference Error
.
Block Scoping With let and const
If the scope of a variable should be limited to a block, let
and const
are recommended.
If (true) {
let blockVar = "I am inside the block";
}
console.log (blockVar); // Reference Error: blockVar is not defined
In this case, you can only reference blockVar
within the block it is initially declared. As soon as you try to access it outside the if block, it will result in a Reference Error
.
Function Hoisting
Functions in JavaScript are also hoisted; however, there is a difference between function declarations and function expressions in terms of how they are hoisted.
Function Declarations
In the case of function declarations, both the name along with the body of the function are raised to the top level of the scope. As a result, you can call a function even before its declaration:
Greet(); // Output: "Hello, world!"
Function Greet () {
console.log ("Hello, world!") ;
}
In this example, the identifier for the Greet
function is hoisted to the top, allowing access to its definition at runtime. Notably, the function call occurs before the actual definition in the code, yet it works due to hoisting.
Function Expressions
Function expressions are not fully hoisted, meaning you may encounter an error if you attempt to call a function expression before it is defined within its scope. Specifically, while the variable to which the function is assigned is hoisted, the actual function definition is not.
sayHello (); // TypeError: say Hello is not a function
Var sayHello = function () {
console.log("Hello!");
};
This code behaves as if it were written like this:
Var sayHello;
sayHello (); // TypeError: say Hello is not a function
sayHello = function () {
console.log("Hello!");
};
In the example above, the declaration var sayHello
gets hoisted to the top of the scope. However, when we try to execute the function assigned tosayHello
, since it has not yet been assigned, it will result in a Type Error
. Thus, you cannot call a function expression before its definition.
Conclusion
Hoisting is one of those interesting JavaScript concepts that can make your life both easier and harder as a JS programmer. Understanding how hoisting works with var
, let
, const
, and function declarations can help you avoid tricky bugs in JavaScript code.
Opinions expressed by DZone contributors are their own.
Comments