Scoping & Hoisting in JavaScript - GeeksforGeeks

16 Dec, 2021
Prerequisite : Understanding Javascript Scoping, Javascript HoistingDo you know what value will be printed on the console when the following piece of code will be executed?
Javascript
var x = 10;function test(){    var x = 20;} test();console.log(x);
If your answer is 10, then you are right. Here the variable ‘x’ declared (and of course initialized) outside the function ‘test’ has a global scope and that’s why it is accessible anywhere in this scope globally. However, the one declared and initialized inside the ‘test’ function can be accessible only inside that function. So the below code snippet will print 20 on the console upon execution.
Javascript
var x = 10; function test(){    var x = 20;    console.log(x);} test();
Now try to guess the output for this one.
Javascript
var x = 10; function test(){    if (x > 20) {        var x = 50;    }     console.log(x);} test();
If you have guessed 10 following the scoping logic discussed in the previous examples, then you are unfortunate as 10 is not the correct answer. This time it will print ‘undefined’ on the console. This is because of the combined effect of variable scoping and variable hoisting. 
Scoping in JavaScript
Let us first understand scoping. Scope is a region of the program where a variable can be accessed. In other words, scope determines the accessibility/visibility of a variable. Since JavaScript looks like a C-family language, it is very obvious to think that scoping in JavaScript is similar to that in most of the back-end programming languages like C, C++ or Java.Let’s consider the following code snippet written in C: 
C
#include<stdio.h>void doSomething(); int main() {     doSomething();    return 0; } // This function examplifies// the block-level-scope in C language void doSomething() {     int x = 10;    printf("%d\n", x);     if (x == 10) {        int x = 20;        printf("%d\n", x);       }     printf("%d\n", x); }
Output :
10
20
10
Here the output is so, because C, as well as the rest of the C-family, has block-level-scope. Whenever the control enters into a block, such as if-block or a loop (e.g., for, while or do-while), the language allows new variables to be declared that can be visible within that block only. Here the variables declared in the inner scope do not affect the value of the variables declared in the outer scope. But this is not the case in JavaScript.
Let’s consider the following JavaScript code :
Javascript
var x = 10;console.log(x); if (true) {    var x = 20;    console.log(x);} console.log(x);
Output :  
10
20
20
Unlike the previous code, written in C, here the last output changes depending on the value assigned to the variable ‘x’ inside the if-block. This is because JavaScript does not have block-level-scope. It has function-level-scope. That means blocks such as if-statements and loops do not create new scopes in JavaScript. Rather a new scope is created only when a function is defined. This sometimes creates confusion for developers who are working with C, C++, C# or Java-like languages. Luckily, JavaScript allows function definitions to go inside any block. For example, we can write the above code by implementing an IIFE (Immediately Invoked Function Expression) inside the if-block that will result in a different output.
Javascript
var x = 10;console.log(x); if (true) {    (function() {        var x = 20;        console.log(x);    })();} console.log(x);
Output :  
10
20
10
In this case, when the control enters into the anonymous function defined and invoked inside the if-block, it creates a new scope. The variable ‘x’ declared inside this scope does not affect the value of the variable ‘x’ declared in the outer scope. Though this is a quite flexible way for creating temporary scopes wherever required, it is not qualified as a good coding style. Hence, to keep the things simple ES6 has introduced two new keywords- ‘let’ and ‘const’, to declare block-scoped variables. When a variable is declared using ‘const’ or ‘let’, it is visible only inside the particular block in which it is declared. For example:
Javascript
var x = 10;console.log(x); function test(){    var x = 20;    console.log(x);     if (x > 10) {        let x = 30;        console.log(x);    }     console.log(x);} test();console.log(x);
Output :  
10
20
30
20
10
In this case, when the test() function is invoked the console.log() statement written inside the if-block prints 30 on the console, whereas the one that follows the if-block inside test() function prints 20 on the console. This means that the variable ‘x’ declared and defined using let keyword has no effect on the value of the variable ‘x’ declared outside its scope, i.e. the if-block. The keyword ‘const’ also operates in a similar manner. The only difference between ‘let’ and ‘const’ is- const is a signal that the identifier won’t be reassigned (The reason that the word ‘identifier’ is preferred here over ‘variable’ is since ‘const’ is used to declare the identifier, it is no longer a variable. It is a constant.), whereas let is a signal that the variable may be reassigned.Now let’s go back to the following example provided previously in this article.
Javascript
var x = 10; function test(){    if (x > 20) {        var x = 50;    }     console.log(x);} test();
Output :  
undefined
We have already discussed why in this example the console.log() statement did not print 10 on the console. Since JavaScript (especially the var keyword) has function-level-scope, the variable ‘x’ declared inside the if-block is visible throughout the function test(). So when the console.log() statement executes, it tries to print the value of the inner ‘x’ rather than the value of the one declared outside the function definition. Now the question is if this code snippet prints the value of the inner ‘x’ then why is it printing ‘undefined’? Here the inner ‘x’ is both declared and defined inside the if-block which is evaluated to false and in JavaScript an attempt to access a variable before its declaration results in a ReferenceError. Then how come the variable is even getting declared inside the function allowing it to be executed without any error? Does the function-level-scope make the conditional statements, like if-else, ineffective and if so, why is not it printing 50, the actual value of ‘x’ declared inside the function? All these questions have a single answer- hoisting. 
Hoisting in Javascript
Hoisting is JavaScript’s default behavior of moving declarations to the top of their containing scope. When a JavaScript code is interpreted, the interpreter invisibly moves (hoist) all the variable and function declarations to the top of the scope they are declared in. However, the location of their definition/initialization/instantiation remains unaffected. For example, the above code snippet will be interpreted as the following before execution.
Javascript
var x;x = 10; function test(){    var x;    if (x > 20) {        x = 50;    }     console.log(x);} test();
In this case, since no value is assigned to the variable ‘x’ declared at the top of the test() function, JavaScript automatically assigns the value ‘undefined’ to it. Since the if-condition is evaluated to false, the ‘console.log()’ statement prints ‘undefined’ on the console.Now let’s look at another example:
Javascript
function test(){    if (false) {        var x = 50;    }     console.log(x);    console.log(y);     var y = 100;    console.log(y);} test();
Output:  
undefined
undefined
100
This code is interpreted as the following.
Javascript
function test(){    var x, y;     if (false) {        x = 50;    }     console.log(x);    console.log(y);     y = 100;    console.log(y);} test();
In the first line of the function body where ‘x’ and ‘y’ are declared in the interpreted code, JavaScript assigns ‘undefined’ to both the variables. Since the if-condition is evaluated to false, the first two ‘console.log()’ statements print ‘undefined’ on the console. However, the last statement prints 100 as it is assigned to ‘y’ before the final ‘console.log()’ executes.Now the important point here is, this variable hoisting mechanism works only for variables declared using var keyword. It does not work for variables or identifiers declared using let and const keywords respectively. Let’s consider the following example. 
Javascript
function test(){    if (false) {        let x = 50;    }     console.log(x);    console.log(y);     let y = 100;    console.log(y);} test();
Output: 
ReferenceError: x is not defined
Identifiers declared using let or const are not at all hoisted. This makes them inaccessible before their declaration in the original source code.
Now a question may come- what is the best practice to declare variables and constants in JavaScript keeping variable hoisting in mind, so that it will never make our code return an unexpected result?Here is the answer. 
All variables and constants that need to be visible within the scope of a particular function should be declared using ‘var’ and ‘const’ keywords respectively at the top of that function. 
Inside blocks (conditional statements or loops) variables and constants should be declared on the top of the block using ‘let’ and ‘const’ respectively. 
If in a particular scope multiple variables or constants need to be declared, then declare them in one go by using a single ‘var’ or ‘let’ or ‘const’ keyword with comma separated identifier names, e.g., var x, y, z; // declaring function-scoped variables let a, b, c; // declaring block-scoped variables const u, v, w; // declaring block-scoped constants 
Though the last point has nothing to do with the consequences of variable hoisting, it is a better practice to keep it in mind while writing code to keep the code clean.
Like variables, functions are also hoisted in JavaScript. However, how the hoisting will be done depends on the way a function is declared. JavaScript allows developers to define a function in two ways- function declaration and function expression.
Let’s consider the following example.
Javascript
function test(){    foo();    bar();     // Function defined    // using function declaration    function foo()    {        console.log('foo');    }     // Function defined    // using function expression    var bar = function() {        console.log('bar');    }} test();
Output: 
foo
TypeError: bar is not a function
The JavaScript interpreter interprets the above code as follows. 
Javascript
function test(){    function foo() {}    var bar;     foo();    bar();     // Function defined    // using function declaration    function foo()    {        console.log('foo');    }     // Function defined    // using function expression    bar = function() {        console.log('bar');    }} test();
Since function foo() is defined using function declaration, JavaScript interpreter moves its declaration to the top of its container scope, i.e., the body of test(), leaving its definition part behind. The definition is dynamically assigned when foo() is invoked. This leads to the execution of the body of foo() function when the function is called. On the other hand, defining a function using function expression is nothing other than variable initialization where the function is treated as the value to be assigned to the variable. Therefore it follows the same hoisting rule which is applicable to variable declarations. Here the declaration of the variable ‘bar’ is moved to the top of its container scope while the place of assigning the function to it remains unchanged. JavaScript cannot interpret a variable as a function until it is assigned a value that is actually a function. This is why trying to execute the statement bar(); before ‘bar’ is defined results in a TypeError.
Though the decision regarding which way one should use to define a function solely depends on developer’s own choice, it is better to keep the following things in mind. 
Define all the functions using function declaration method on the top of their container scope. This not only keeps the code clean but also ensures that all the functions are declared and defined before they are invoked. 
If you must have to define a function using function expression method, then make sure that it is defined before it is invoked in the code. This will eliminate the chance of unexpected outputs or errors that may result due to hoisting.
sumitgumber28
sweetyty
JavaScript-Misc
JavaScript
Technical Scripter
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Remove elements from a JavaScript Array
Convert a string to an integer in JavaScript
Difference between var, let and const keywords in JavaScript
Differences between Functional Components and Class Components in React
Difference Between PUT and PATCH Request
How to read a local text file using JavaScript?
Node.js | fs.writeFileSync() Method
How to Use the JavaScript Fetch API to Get Data?
JavaScript | Promises
How to get character array from string in JavaScript?