Inquisitive Developer - JavaScript: Scoping And Hoisting - Part One

Preface

First, a big thanks to all of you for supporting my last series,

I enjoyed the interaction with all of you -- reading your comments, questions, and messages. I feel excited and I am starting a new series called “Inquisitive Developer – JavaScript”. This new series will go in depth about JavaScript programming language. I will try my best to take you on this programming journey and cover common mistakes, misconceptions, and cool features of the King of scripts, JavaScript.

I welcome you all to the new series!

Getting started

Let me start with a basic keyword ‘var’.

> var variableName = ‘assignment value’;

There are two parts here, i.e. declaration and initialization. I will split them as:

> var variableName; // Code 1
> var variableName = ‘assignment value’; // Code 2


Code 1 will declare variable only and the default value assigned is ‘undefined’. JavaScript assigns undefined value, as the purpose is to assign default value to this variable. The benefit of undefined is that a developer may not know the value of the variable during declaration.

Code 2: JavaScript interpreter interprets it as declared and initialized together,

> var variableName;
> variableName = ‘assignment value’;

To understand ‘hoisting’ we will first learn scoping.

Scoping

JavaScript follows lexical scoping (static scoping). Lexical means vocabulary of a language. Scoping means the scope of a variable. There are two types of variable scopes global and local.

Global scope

A variable that is declared outside the function. This variable can be accessible throughout the program, for example,

  1. var x = 90; //   
  2. function f1() {  
  3. console.log(x);  
  4. };  
  5. f1(); // output will be 90  
In above example x is created in global scope and is accessible by f1()

Any variable created in global scope is added to global-context window object. You can check as,
  1. var x = 90;  
  2. function f1() {  
  3. console.log(x);  
  4. };  
  5. f1();  
  6. console.log(window.x); //output 90  
Now I can remove ‘var’ and create x variable.

Question: Will it work and give the same output?

Ans: Yes, it works well because we are operating at non-strict mode currently. It is recommended to use “use strict” mode, but currently I am not bothered about it.
  1. x = 90;  
  2. function f1() {  
  3. console.log(x); //output 90  
  4. };  
  5. f1();  
Local scope (Function-level scope)

A variable that is declared inside the function. Seems straightforward, here is the demonstration,
  1. function f1() {  
  2. var x = 1; // x is a local variable  
  3. console.log(x); // prints 1  
  4. } f1();  
Let us try to use block scope which we generally use in other programming languages like C, C++, C#,
  1. function f1() {  
  2.     var x = 1;  
  3.     console.log(x); // print 1  
  4.     if (true) {  
  5.         var x = 2;  
  6.         console.log(x); // print 2   
  7.     }  
  8.     console.log(x); // print 2   
  9. };  
  10. f1();  
Above we declared x again inside if condition. In C or other programming languages there is a block scope (curly braces) so outside if x value should remain 1 it will print 2.

No block-level scope

In JavaScript, there is no block scope and it is local scope within function. Function creates scope in JavaScript. Makes it trickier,
  1. function f1() {  
  2.     var x = 1;  
  3.     console.log(x); // print 1  
  4.     function f2() {  
  5.         if (true) {  
  6.             var x = 2;  
  7.             console.log(x); // print 2   
  8.         }  
  9.     };  
  10.     f2();  
  11.     console.log(x); // print 1   
  12. };  
  13. f1();  
We created another function called f2() within f1(), therefore a new function scope is created.

A misconception

One of the most popular codes which all of us have written in our learning development is FOR loop.
  1. for (var index = 0; index < 5; index++)  
  2. {  
  3.     console.log(index); // will simply print 0 .. 4  
  4. }  
We think that the scope of index variable is just limited to FOR loop but it is incorrect. Here is a program,
  1. console.log(index); // print undefined  
  2. for (var index = 0; index < 5; index++)  
  3. {  
  4.     console.log(index); // print 0 .. 4  
  5. }  
  6. console.log(index); // print 5  
The first line prints undefined because of a concept variable “hoisting”. I will talk about it shortly. The last line prints 5 because index is not only restricted to FOR but because there is block-level scoping in JavaScript. Therefore, index is accessible throughtout the scope.

There is a concept in JavaScript called “Hoisting” which we will review.

Priority

Local variable gets priority over global variable. If you declare a variable with the same name in the below example, which is using anonymous function via arrow ECMAScript 6, the variable index is created inside anonymous and outside anonymous function. The output will depend upon the scope of variable,
  1. var index = 9;  
  2. (() =>  
  3.  {  
  4.     for (var index = 0; index < 5; index++)  
  5.     {  
  6.         console.log(index); // print 0..4  
  7.     }  
  8. })();  
  9. console.log(index); // print 9  
Variable Hoisting

JavaScript variables are ‘hoisted’. This means the variables will be hoisted at the top regardless of its placement. Hoist means to raise or lift up. Similarly, variables are hoisted on top but not on assignment or initialization. A small code shows that ,
  1. console.log(hoist1); // prints undefined  
  2. var hoist1 = 'hoisting';  
  3. console.log(hoist1); // prints hoisting  
The above is read by JavaScript interpreter in different ways because hoisting is done and variables are hoisted at top.
  1. var hoist1;  
  2. console.log(hoist1); // prints undefined  
  3. hoist1 = 'hoisting';  
  4. console.log(hoist1); // prints hoisting  
Hoisting precedence

An interesting fact about precedence when there exists a variable with the same name and function is that function takes precedence over variable declaration. But if variable initialization is done then it’s of higher order. Example,
  1. var f1; // variable declaration is done  
  2. function f1(params) {  
  3.     console.log('its function1');  
  4. };  
  5. console.log(typeof f1); //print function  
  6. var f2 = 'its variable'// variable assignment  
  7. function f2(params) {  
  8.     console.log('its function2');  
  9. };  
  10. console.log(typeof f2); // print string  
Rules of variable definition 
  • Never create undeclared variables, because these are created as global.
  • Undeclared variables unless assigned value give error if they are referenced, example-

> console.log (a);

error

The reason is all declared variables are hoisted on top,

  • Undeclared variables can be deleted:

    Sample one
    1. a = 10;  
    2. console.log(a); // print 10  
    3. delete a;  
    4. console.log(typeof a); // undefined  
    5. console.log(a); // below error  
    error

    Sample two
    1. var a = 10;  
    2. console.log(a); // print 10  
    3. delete a;  
    4. console.log(typeof a); // number  
    5. console.log(a); // print 10  

Hoisting functions and expression

We learned earlier in the voice of a developer series that function is also hoisted,

The function expression is not hoisted, please refer to code below,
  1. fncHoisted(); // TypeError: undefined is not a function  
  2. fncNotHoisted(); // Definition hoisted!  
  3. function fncHoisted() {  
  4.     console.log("Definition hoisted!");  
  5. }  
  6. var fncNotHoisted = function()  
  7. {  
  8.     console.log("Definition not hoisted!");  
  9. };  
Block level scope

So far we have learnted that var doesn’t have a block scope. But ECMAScript 6 which has let keyword provides block scope.

Scoping rules 
  • Unlike var keyword, let provides block and sub-block's scope. It is similar to other programming languages.
    1. function f1()   
    2. {  
    3.     let name = 'Sumit';  
    4.     if (true) {  
    5.         let name = 'Jolly';  
    6.     }  
    7.     console.log(name); // print Sumit  
    8. }  
    9. f1();  
  • Unlike var, let keyword does not create a property in global object, i.e., window.
    1. var x = 'associate with window object.';  
    2. let y = 'no association.';  
    3. console.log(window.x);  
    4. console.log(window.y);  
    Output

    output

  • Duplicate variables are not allowed,
    1. var y = 10;  
    2. var y = 20; // this is allowed using var  
    3. let x = 10;  
    4. let x = 20; // duplicate x is not allowed  
    error

  • Always good to declare variable using let before referencing it, here you can see the difference:

    Using let

    It will give reference error,
    1. function do() {  
    2.     console.log(foo); // ReferenceError  
    3.     let foo = 2;  
    4. }  
    5. do();  
    Using var

    It will print undefined rather than error,
    1. function do()  
    2. {  
    3.     console.log(foo); // undefined  
    4.     var foo = 2;  
    5. }  
    6. do();  

Summary: Hope you enjoyed reading this article. Please share your comments / feedback.