1st let us take a look at Javascript Variable Scope and Hoisting.
For Java developers this would be a strange world by looking at how Variable Scope works in JavaScript.
Eg: Java program to demo variable scope for local and global variables.
Output
Now see how a similar program outputs in Javascript
Output
Variable Hoisting
Why we got undefined instead of Anwar?
In Javascript, all variable declarations are hoisted (lifted and declared) to the top of the function, if defined in a function, or the top of the global context, if outside a function.
Note: Only variable declarations are hoisted to the top, not variable initialization or assignments
So the above JavaScript program is seen by the JavaScript Engine as
Local Variables
In JavaScript, another weird thing about variable scope is its based on Function- level instead of Block-level Scope (variables scoped to surrounding curly brackets);
A JavaScript program to demo the Function level Variable scope
Output
Global Scope if not Declared
Any variable in a function that is not declared will become a global variable.
For Java developers this would be a strange world by looking at how Variable Scope works in JavaScript.
Eg: Java program to demo variable scope for local and global variables.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
| public class studentShow{ private static class Student { private static String name= "Anwar" ; private static void studentName(){ System.out.println( "Inside 1. name= " +name); String name = "Ravi" ; System.out.println( "Inside 2. name= " +name); } } public static void main(String args[]) { System.out.println( "Outside 1. name= " +Student.name); Student.studentName(); System.out.println( "Outside 2. name= " +Student.name); } } |
public class studentShow{
private static class Student
{
private static String name="Anwar";
private static void studentName(){
System.out.println("Inside 1. name= "+name);
String name = "Ravi";
System.out.println("Inside 2. name= "+name);
}
}
public static void main(String args[])
{
System.out.println("Outside 1. name= "+Student.name);
Student.studentName();
System.out.println("Outside 2. name= "+Student.name);
}
}
1
2
3
4
| Outside 1. name= Anwar Inside 1. name= Anwar Inside 2. name= Ravi Outside 2. name= Anwar |
Outside 1. name= Anwar
Inside 1. name= Anwar
Inside 2. name= Ravi
Outside 2. name= Anwar
Now see how a similar program outputs in Javascript
01
02
03
04
05
06
07
08
09
10
11
12
13
14
| var name = "Anwar" // global variable function studentName() { console.log ( "Inside 1. name= " + name); // undefined; Due to Variable Hoisting var name = "Ravi" ; // local variable; only accessible in this studentName function console.log ( "Inside 2. name= " + name); // Ravi } console.log ( "Outside 1. name= " +name); // Anwar: the global variable studentName(); console.log ( "Outside 2. name= " +name); // Anwar: the global variable |
var name = "Anwar" // global variable
function studentName()
{
console.log ("Inside 1. name= "+ name); // undefined; Due to Variable Hoisting
var name = "Ravi"; // local variable; only accessible in this studentName function
console.log ("Inside 2. name= "+ name); // Ravi
}
console.log ("Outside 1. name= "+name); // Anwar: the global variable
studentName();
console.log ("Outside 2. name= "+name); // Anwar: the global variable
1
2
3
4
| Outside 1. name= Anwar Inside 1. name= undefined Inside 2. name= Ravi Outside 2. name= Anwar |
Outside 1. name= Anwar
Inside 1. name= undefined
Inside 2. name= Ravi
Outside 2. name= Anwar
Why we got undefined instead of Anwar?
In Javascript, all variable declarations are hoisted (lifted and declared) to the top of the function, if defined in a function, or the top of the global context, if outside a function.
Note: Only variable declarations are hoisted to the top, not variable initialization or assignments
So the above JavaScript program is seen by the JavaScript Engine as
1
2
3
4
5
6
7
8
| function studentName() { var name; // only declaration is hoisted to top of the function console.log ( "Inside 1. name= " + name); // Since initialization is not hoisted ; // current value is undefined name = "Ravi" ; console.log ( "Inside 2. name= " + name); } |
function studentName()
{
var name; // only declaration is hoisted to top of the function
console.log ("Inside 1. name= "+ name); // Since initialization is not hoisted ;
// current value is undefined
name = "Ravi";
console.log ("Inside 2. name= "+ name);
}
Local Variables
In JavaScript, another weird thing about variable scope is its based on Function- level instead of Block-level Scope (variables scoped to surrounding curly brackets);
A JavaScript program to demo the Function level Variable scope
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
| var age = 15; function studentName() { var name = "Ravi" ; console.log ( "1. Outside Block level name= " + name); //Ravi if (age<18) { var name = "Master. " ; //local variable; //for Java developers, name variable scope should be limited to if block console.log( "Inside if Block - name= " +name); // Master } else { var name = "Mr. " ; //local variable; //for Java developers, name variable scope should be limited to else block console.log( "Inside else Block - name= " +name); } console.log ( "2. Outside Block level name= " + name); // outside if-else block //for Java developers, Output should be Ravi // But since in JS, the there is no block-level variable scope just Function level scope // Master is the value of name } |
var age = 15;
function studentName() {
var name = "Ravi";
console.log ("1. Outside Block level name= "+ name); //Ravi
if(age<18) {
var name = "Master. "; //local variable;
//for Java developers, name variable scope should be limited to if block
console.log("Inside if Block - name= "+name); // Master
}
else {
var name = "Mr. "; //local variable;
//for Java developers, name variable scope should be limited to else block
console.log("Inside else Block - name= "+name);
}
console.log ("2. Outside Block level name= "+ name); // outside if-else block
//for Java developers, Output should be Ravi
// But since in JS, the there is no block-level variable scope just Function level scope
// Master is the value of name
}
1
2
3
| 1. Outside Block level name= Ravi Inside if Block - name= Master. 2. Outside Block level name= Master. |
1. Outside Block level name= Ravi
Inside if Block - name= Master.
2. Outside Block level name= Master.
Global Scope if not Declared
Any variable in a function that is not declared will become a global variable.
01
02
03
04
05
06
07
08
09
10
11
12
| var name = "Anwar" // global variable console.log ( "1. Outside name= " + name); function studentName() { name = "Ravi" ; // global variable // If you don't declare your local variables with the var keyword, // they are part of the global scope console.log ( " Inside name= " + name); //Ravi } studentName(); console.log ( "2. Outside name= " + name); |
var name = "Anwar" // global variable
console.log ("1. Outside name= "+ name);
function studentName() {
name = "Ravi"; // global variable
// If you don't declare your local variables with the var keyword,
// they are part of the global scope
console.log (" Inside name= "+ name); //Ravi
}
studentName();
console.log ("2. Outside name= "+ name);
1
2
3
| 1. Outside name= Anwar Inside name= Ravi 2. Outside name= Ravi |
1. Outside name= Anwar
Inside name= Ravi
2. Outside name= Ravi
All non-declared variables in JavaScript will pollute the global scope so its best pratice to declare as local variables inside Functions.
What is a closure?
A closure is an inner function that has access to the outer (enclosing) function’s variables & parameters.
The closure has three scope chains:
- it has access to its own scope (variables & parameters defined in its Function),
- it has access to the outer function’s variables & parameters, and
- it has access to the global variables.
Note : Inner function cannot call the outer function’s arguments object
eg: JS Demo of Closure
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
| function studentTopRanks(firstRank, secondRank) { var msg = "The Rank list : " ; function print() { // print() is closure inner function // It has access to local variables of enclosed outer function // It also has access to parameters - firstRank, SecondRank // of enclosed outer function return msg + "1st rank - " + firstRank + ", 2nd rank - " + secondRank; } return print(); } studentTopRanks( 'Ravi' , 'Anwar' ); |
function studentTopRanks(firstRank, secondRank)
{
var msg ="The Rank list : ";
function print()
{
// print() is closure inner function
// It has access to local variables of enclosed outer function
// It also has access to parameters - firstRank, SecondRank
// of enclosed outer function
return msg + "1st rank - " + firstRank
+ ", 2nd rank - " + secondRank;
}
return print();
}
studentTopRanks('Ravi','Anwar');
Weirdness of Closures in JavaScript
1. Closures have access to the outer function’s variable even after the outer function returns
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| function studentTopRanks(firstRank) { var msg = "The Rank list : " ; function print(secondRank) { // print() is closure inner function // It has access to local variables of enclosed outer function // It also has access to parameters - firstRank, SecondRank // of enclosed outer function return msg + "1st rank - " + firstRank + ", 2nd rank - " + secondRank; } return print; // return print; we return the print function object, instead of // return print(); which returns the String of the msg } var printObjVar = studentTopRanks( 'Ravi' ); // At this juncture, the studentTopRanks outer function has returned. // It returns a function Object and assigned to variable printObjVar var display = printObjVar( "Jackson" ); // The closure (print) is called here after the outer function has returned above // Yet, the closure still has access to the outer function's variables and parameter console.log(display); |
function studentTopRanks(firstRank)
{
var msg ="The Rank list : ";
function print(secondRank)
{
// print() is closure inner function
// It has access to local variables of enclosed outer function
// It also has access to parameters - firstRank, SecondRank
// of enclosed outer function
return msg + "1st rank - " + firstRank
+ ", 2nd rank - " + secondRank;
}
return print;
// return print; we return the print function object, instead of
// return print(); which returns the String of the msg
}
var printObjVar = studentTopRanks('Ravi');
// At this juncture, the studentTopRanks outer function has returned.
// It returns a function Object and assigned to variable printObjVar
var display = printObjVar("Jackson");
// The closure (print) is called here after the outer function has returned above
// Yet, the closure still has access to the outer function's variables and parameter
console.log(display);
2. Closures store references to the outer function’s variables; not the actual value.
Douglas Crockford demonstrated the use of Private Members in JavaScript using this technique.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| function Student() { var name = "Master" ; return { // We are returning an object with some inner functions // All the inner functions have access to the outer function's variables getName: function () { // This inner function will return current value of // outer functions name variable // Even after the setName function changes it return name; }, setName: function (newName) { // This inner function will change the outer function's variable anytime name = newName; } } } var studObjVar = Student(); // At this juncture, the Student outer function Object has returned. studObjVar.getName(); // Master studObjVar.setName( "Ravi" ); // Changes the outer function's variable studObjVar.getName(); // Ravi: It returns the updated celebrityId variable |
function Student()
{
var name = "Master";
return {
// We are returning an object with some inner functions
// All the inner functions have access to the outer function's variables
getName: function()
{
// This inner function will return current value of
// outer functions name variable
// Even after the setName function changes it
return name;
},
setName: function(newName)
{
// This inner function will change the outer function's variable anytime
name = newName;
}
}
}
var studObjVar = Student(); // At this juncture, the Student outer function Object has returned.
studObjVar.getName(); // Master
studObjVar.setName("Ravi"); // Changes the outer function's variable
studObjVar.getName(); // Ravi: It returns the updated celebrityId variable