In this article, we will explain what is a Scope in JavaScript and how it affects the visibility of variables. JavaScript creates four types of Scopes. They are Global Scope, Module Scope, Block Scope & Function Scope. The Block Scope & Function Scope are also called Local Scope.
Table of Contents
What is Scope
The scope is a region of the program where a variable is visible.
Every variable we define will become part of a scope. We can access that variable only within its scope.
For Example, we have nameVar
variable inside the function. When we access it outside the function, JavaScript will throw the ReferenceError. That is because JavaScript creates a new scope for every function. In this example, nameVar
become part of the someFunc
scope. Hence you cannot access it outside it.
1 2 3 4 5 6 7 8 | function someFunc() { let nameVar="Test" } someFunc(); console.log(nameVar); //Uncaught ReferenceError: nameVar is not defined |
It is very important to understand, how & when JavaScript creates scopes. Because it determines where you can access the variables that you define
Need for Scopes
Scopes restrict the visibility of variables. A local variable defined in a function cannot be accessed in another function or outside of it. This will help other parts of the code accidentally modify the value of a variable or create a new variable with the same name. Without scopes, this would create hard-to-track bugs.
Types of scope in JavaScript
There are four types of scope in Modern JavaScript.
- Global Scope
- Function Scope
- Block Scope
- Module Scope
The Block Scope and Modules are introduced by ES6. Before that, Javascript had only two Scopes. Global & Function Scope (also known as Local Scope).
Global Scope
The global scope is the root scope of the Application. JavaScript automatically creates the global scope when the app starts. It is the Parent of all other scopes.
Variables that we declare inside the Global Scope, are visible everywhere. You can access it from anywhere in our code. These variables are called Global Variables.
We can create a global variable when we declare the variable outside of a function, block statement ( {}
), or a Module.
For Example, the code below globalVar
is a global variable. . We can access it from within the function, inside a { }
, inside if block, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | let globalVar="Hello" //Functions function someFunc() { console.log(globalVar) //Hello } someFunc(); //Code block { console.log(globalVar) //Hello } //if,for,switch also creates an code block if (true) { console.log(globalVar) //Hello } console.log(globalVar) //Hello |
Local Scope
The Scope, which is created only for a specific part of the code is Local Scope. JavaScript creates two types of Local Scopes. One is for a Function (Function Scope) and the other one for a block statement (block Scope)
The Variables declared within the local scope are called Local Variables.
Function Scope
The JavaScript creates a new Function Scope for every JavaScript Function. The variables we declare inside the function will become part of the function scope. We cannot access it outside the function.
You can use either var
, let
or const
to create a variable inside the function.
Even the function parameters also become part of the function scope. Hence only available inside the function and not outside.
For Example, In the following example, funcVar
becomes part of the someFunc
Function scope. We cannot access it outside the someFunc
. Trying to access will result in ReferenceError
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | //Functions function someFunc() { let funcVar="Hello from Function" console.log(funcVar) //Hello from Function } someFunc(); //All Codes below throws an exception //Uncaught ReferenceError: funcVar is not defined //Function function someOtherFunc() { console.log(funcVar) } someOtherFunc(); //Code block { console.log(funcVar) } //if,for,switch also creates an code block if (true) { console.log(funcVar) } //Global console.log(funcVar) |
Block Scope
JavaScript creates a new Block Scope for every Block statement or code block. A block statement is a group of statements inside a pair of braces( { }
curly brackets). The let
& const
variables we declare inside the Block statement will become part of the Block scope. We cannot access it outside the Block statement.
Block Scope has become part of the JavaScript from ES6. Before that, we had only two scopes. function & global
The Block Scope rules apply only to variables declared with the let
or const
keywords. If you use the var
to declare the variable, then it will become part of the parent scope.
For Example, In the following example, blockVar
becomes part of the block scope. Trying to access it outside the block statement will result in ReferenceError
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | //Code block { let blockVar="Hello for blockVar" console.log(blockVar) //Hello for blockVar } //All Codes below throws an exception //Uncaught ReferenceError: blockVar is not defined //Function function someFunc() { console.log(blockVar) } someFunc(); //Code block { console.log(blockVar) } //if,for,switch also creates an code block if (true) { console.log(blockVar) } //Global console.log(blockVar) |
The block scope is formed wherever you find the { }
. For example, bodies of if/else statements, while loops, for loops, etc
In the For Loop below, the body of the loop is enclosed in a { }
, hence forms a block scope. Here the variable test
is not accessible from the outside. The Loop variable (i
) also become part of the scope, although it appears outside of the curly braces.
1 2 3 4 5 6 7 8 9 10 11 12 | for (let i = 0; i < 5; i++) { let test="hellp" console.log(i); } //Both will result in error //console.log(test) //error //console.log(i) //error |
Block Scope with var
The block scope does not apply to the var
variable. In the above code change the let
keyword with var
keyword. Now the blockVar
becomes the global variable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //Code block { var blockVar="Hello for blockVar" console.log(blockVar) //Hello for blockVar } //All the code below works becuase blockVar is now global variable //Function function someFunc() { console.log(blockVar) //Hello for blockVar } someFunc(); { console.log(blockVar) //Hello for blockVar } if (true) { console.log(blockVar) //Hello for blockVar } console.log(blockVar) //Hello for blockVar |
In the following example, the test variable is declared within a block statement, which is in turn within a function. Since block scope does not apply to var
, test
variable will become part of the parent scope, which is addNum
function. That is why accessing test
outside the function will result in an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 | function addNum() { { var test="Hello" } console.log(test) //Hello } addNum(); console.log(test) //Error |
Scope inside another scope is a nested Scope or Scope chain.
In the example of for loop, we used var
instead of let
. Now both loop variable i
and test
becomes the global variable.
1 2 3 4 5 6 7 8 9 10 11 | for (var i = 0; i < 5; i++) { var test="hellp" console.log(i); } //no error console.log(test) //Hello console.log(i) //5 |
Module Scope
A Variable we declare inside the modules, but outside of any function becomes part of the Module Scope. Other Modules can access it only if we explicitly export it.
We will learn about module in a future article.
Scope Chain
A nested Scope or Scope chain is formed when we create a scope inside another scope. For Example, a function inside a function, block statement inside a function or function inside a block statement, etc.
Global scope is the root scope of all other scopes.
The variable defined in the parent scope is accessible in the child scope. This is called Lexical scope. But the parent cannot access the properties of its child scope.
The following example has three functions. funcGrandChild
is inside the funcChild
, which in turn inside the funcParent
. Each function has its own scope. These scopes form a scope chain.
The funcGrandChild
can read all the variables defined by the parent scopes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | x=10 function funcParent() { let a = 1; funcChild = function () { let b = 2 funcGrandChild = function () { let c = 3 //child can access all the variables console.log("funcGrandChild",x,a,b,c) } funcGrandChild() console.log("funcChild",x,a,b) //Error Cannot access c //console.log("funcChild",c) } funcChild() console.log("funcParent",x,a) //Error Cannot access b & c //console.log("funcChild",b) //console.log("funcChild",c) } funcParent(); //You can only access x here. |
1 2 3 4 5 6 7 8 9 10 11 | Obj = { value: 2, func: function () { console.log(value) } } Obj.func(); |
Shadowing
If we declare a variable that has the same name as one from the parent scope, the variable from the parent scope becomes invisible in the current scope and all its child scopes
Inside the somFunc(), the global x is invisible as we have declared a new local variable x.
1 2 3 4 5 6 7 8 9 10 11 12 | let x = "global"; function somFunc() { let x = "local"; console.log(x); // local //We cannot access the global x here } somFunc(); console.log(x); // global |
Declaring varibles without keyword
You can declare a variable, without using var
, let
or const
keywords in JavaScript. Such a variable will become part of the global scope implicitly.
For Example, take this for Loop. We have forgotten to use the keyword in the declaration of variable i. This is one of the common mistakes. The variable i
becomes part of the global scope.
1 2 3 4 5 6 7 8 | //Example 1 for (i=0; i < 10; i++) { // do something } console.log(i) //10 |
Here is another example. The j
variable also becomes part of the global scope.
1 2 3 4 5 6 7 8 9 10 11 | //Example 2 function someFunc() { j=10 } someFunc() console.log(j) //10 |
These variables stay in the memory until we unload the page. It is an even bigger problem in Single Page applications where we never refresh the page.
You can avoid such problems by declaring the “use strict”. This option added in the ES5 version of the JavaScript. The following code results in an error.
1 2 3 4 5 6 7 8 9 10 | "use strict" //Example 1 for (i=0; i < 10; i++) { // do something } //Uncaught ReferenceError: i is not defined |