JS: Scope & Closure

Easiest and Funniest way to

Understand JavaScript scope, hoisting, closure with performance secret.

May 18, 2014

I promise, you will be able to explain the following concepts to your grandma.

  1. Global Scope
  2. Local Scope
  3. Multiple Scope
  4. Access a Variable
  5. Access Global Variable
  6. Hoisting
  7. Closure
  8. Don't have time! Read Summary and get lost

1. Global Scope

Global:: When someone says global, what comes to your mind? You may visualize a globe you remember seeing in high school. For those who never went to a high school, have a look at Image -1.

Global Star:: Look at image -2. If you cant see a person in the top right corner, borrow a magnifying glass from your grandma - it's Jackie Chan, the celebrity. He is a global star. People will recognize him from any part of the world. Similarly, you can easily recognize a global star like Nelson Mandela, Princess Diana, etc.

Global Variable:: Global variable is exactly like global star. You can access it (get or set the value of it), from any part of your application.

Image-1: The Globe

Image-2: The Global Star

Image-3: Mr. Jackie Chan


Global Events:: If you are not a genius like me, you can easily recognize global events like "New Year's Day", "Christmas", "The Olympics", etc. from any part of the world. Have a look at Image -5.

Global Functions:: Similarly, global functions could be accessible from any part of your application.

Global Scope:: Anything in the global scope could be accessible accross your application. Have a look into Image -6. You can have global variables like global stars and global functions like global events/activities.

Image-4: Happy New Year

Image-5: The Global Things

Image-6: The Global Scope


In the code block below, "a" is a global variable and "b" is a global function. You can access global variable from anywhere of your application including inside of the function "b". Anything that is not inside of a function, is global.


//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

b(); //execute global function.
          

Take away

Global variables are exactly like global stars. You can access them (get or set the value of it), from any part of your application. Global functions are the same as global events. You can execute (call) them from any part of your application.

Global variables and functions are executed in the global context. If you need to understand more about JavaScript context, read Execution context and Stack in JavaScript or watch Understanding JavaScript this in a crystal clear way

2. Local Scope

Local:: When somebody says local, what comes to your mind? If you are smart like me, you will thik- not global. Envision about a part of the world. For example, the USA, it's a country, or a part of the world ( If you don't know where is the USA is located, I marked it for you in Image -7).

Local Star:: Look at image -8. If you are in the USA, you may know this infamous celebrity ( she somehow manages to make the tabloids). But people outside of the USA will not recognize her. She is a local star, boudn to her territory.

Local Variable:: Local variables are exactly like local stars. You can only access one (get or set the value of it)- in the local scope. In JavaScript local scope is defined by a function. If you declare a variable inside a function, you can only access it (get or set the value) inside of the function. If you try to access it from outside of the function, you will get a reference error.

Image-7: The Local

Image-8: Kim Kardashian

Image-9: The Local Star


Local Events:: Consider American football (Image-10). Not the football (soccer) the rest of the world knows. It's crazily famous here in the USA, particularly the final events, the Super Bowl. But people outside of the world doesn't know much about it, making the Super Bowl a Local Event.

Local Functions:: Similarly, any function inside another function is called a local function. You can only execute it inside the outer function. If you really want to get the inner function, you have to return the function by creating closure (stay tuned for closure information).

Local Scope:: Anything in the local scope (inside a function) is only accessible from within. Have a look at Image-11.

Scope Chain:: Local scope is connected to the global scope. In Image-12, see a dotted line/chain towards the global scope. This is how you have access to any global variables/functions. This link/chain is called the scope chain (stay tuned for more on the scope chain).

Image-10:Local Events

Image-11:Local Events

Image-12:Local Scope

In the code block below, "d" is a local variable. You can access it (get or set the value) inside the function "b". If you try to access it outside of the function, you will get a reference error.


var a = 2; 
function b(){
   var d = 21; //local variable
   console.log(d); //can access it inside the function
}
b();  // 21

console.log(d); //ReferenceError: d is not defined
          

Implicit Global:: If you forget to put var before the variable of a function, it becomes a global variable. In the code block below, "a", "f" and "i" are global variables. However, you may not want "f" and "i" to be global. So, be careful. "f" becomes global variable as you put ";" after the value of "e", you might want to add a comma in this place.


var a = 21;

function b(){
  var d = 7,
      e = 8;
      f = 9;

  for(i =0; i<5; i++){
    console.log(e);  
  }  
}
         

Take away

Local variables are exactly like local-stars. You can only access them (get or set their values) inside their scope. Local functions are like local events. You can only execute (celebrate) inside the scope.

Local variables and functions are executed in the local context.

3. Multiple Scope

Multiple functions:: If you have more than one functions and you execute them, each one of them will create their own scope. However, every execution scope will be linked to the global scope. Thats is how you have access to the global scope variable and function from any part of your application. Have a look at Image -13.

Nested Scope:: If you execute a function within a function, the inner function will create a scope inside the outer function. Look at the Image-14. Remember, the inner scope will have access to the outer function variable, while the outer one will have access to the global variable. Now take a minute to absorb Image-14 and take another minute to sift through your confusion.

Closure:: If you have a function inside a function, this is called closure. Closure is a powerful feature of JavaScript for encapsulation. Some of your function could be closure (Image-15). Don't worry about it for now, Soon you will have a crystal clear understanding of closure.

Image-13: Multiple Scope

Image-14: Nested Scope

Image-15: JavaScript Scope


In the code block, below function "b" and function "c" create separate scopes like you see in Image-13


var a = 2;

function b(){ 
  console.log(a); 
}

function c(){ 
  console.log(a); 
}

b(); // scope created for executing function "b" 
c(); // scope created for executing function "c" 
         

In the code block below, when executing of function "b", function "dog" will be executed and it will create a new scope within the scope of function "b". Refer to Image-14


var a = 2; 
function b(){ 
   var d = 21;
   function dog(){  console.log(a); }
   dog(); //closure is created
}

function c(){ console.log(a); }

b();            
c();
         

Block Scope

Block scope is a block/ part of a code surrounded by {}. JavaScript doesn't support block scope. Read more about block scope at MDN: block scope


function getY(){
  var x = 5;
  if(x>0){
    var y = 7;  
  }
  return y;
}

getY(); //7
          

Congratulations to you! Because JavaScript does not support block scope, it is that much easy to understand. It is less complex and faster to parse. Hoisting also plays a role in this, but we will have to come back to that another time (sorry, too many promises).

ES6: let

JavaScript cannot use block scope but you can have a block scope variable when ES6 "let" is supported by major browsers. Read MDN documents to learn more about let.

Take away

You will have as many function scopes as the functions you have executed. Functions inside functions create nested scopes inside the outer function scopes. There would be a new scope, even a function calls itself. However, you will have only one global scope.

4. Access a Variable

Consider the situation - you wake up Saturday afternoon and start looking for car keys ( for whatever reason, you completely forgot what you did Friday night). You might search in the following steps-

If you find the keys inside your room you will not go to check the keys holder or outside.

Image-16: Searching Key

Image-17: Key Holder

Image-18: OutSide

Why are you telling this stupid story every weekend?

If you ask for a variable inside a function, JavaScript doesn't know right away whether this variable is a global variable, a local variable or a parameter. So, it has to follow these same steps. Every single times.
  • First (Local Scope): It checks the local variables. If it is in the local scope, JavaScript will grab it and give it to you.
  • Second (Parameters): If the variable doesn't exists in the local scope, it will then check the the parameters. (Similar to keys in the key holder in Image-17)
  • Third (Outer Scopes) If the variable you are asking for doesn't exists in the parameter, JavaScript starts walking outside of the function via the scope chain of the current scope. It does this until it hits the end of the chain. The end of the scope is called the "window" object in JavaScript.
  • Fourth (Window): If the variable exists in the global scope, it would be a property of the global object "window". JavaScript will grab it and give it to you.
  • Fifth (Undefined): If the variable doesn't exists in the window object, JavaScript will respond with "undefined". This means the variable you are looking for, doesn't exists.

Since local scope is the first place to look for a variable, the local variable gets priority over global and parameter.


var x = 9;

function getX(x){
  var x = 7;
  return x;  
}

getX(5); // 7
         

Take Away

The order of accessing a variable is local variables (inner scope), parameters, outer scope(s) then as a last resort, the global variable (window). If your variable doesn't exist in the global scope, JavaScript will return an undefined.

5. Global Variable Performance

Climbing through the Scope Chain: If you are asking for a global variable from inside of a function, you are climbing through the scope chain. In the code example you are climbing the scope chain for 1,000,000. Every single time, you have to check the local scope, parameters and then get outside of the function. If you are inside of multiple nested functions, then it becomes even more expensive.


var myArr = [0,1,2,3, ...., 1000000];

function getSum(){
  var sum = 0;
  for (var i =0; i<1000000; i++){
    sum += myArr[i];
  }    
  return sum;
}

getSum();
          

In such expensive cases, you can have a local copy of the global variable. In that case you dont have to climb through the scope chain again and again. Avoid some unnecessary walking.


var myArr = [0,1,2,3, ...., 1000000];

function getSum(){
  var sum = 0;
  var localArr = myArr;
  for (var i =0; i<1000000; i++){
    sum += localArr[i];
  }    
  return sum;
}

getSum();
          

Take Away

If you are accessing global variable over and over, have a local copy of the global variable. If you are doing it just once or twice, you can ignore these minor performance tips.

6. Hoisting

Hoist is a verb meaning to raise. A hoist is also a noun, for example a rope and pulley (Image -19). This type of hoist is used in the factories to raise heavy things. One benefit of raising something to the ceiling is that you can see it from any part of the room/factory. You can also hang/hoist your bike for storage during the winter (Image-20)

JavaScript does the exact same thing to hoist (raise) a variable declaration and a function declaration to the top of a function scope. Those variables and functions can be used from any part of the function without raising no reference Exception.

Image-19: Mechanical Hoist

Image-20: Hoisting Bike

Image-21:Ant Hoist Power

In the code block below, the variable "b" is declared after the return statement. If you execute the foo function, you will not receive the no reference error. If you dont trust me, copy this code and run it at your console.


function foo(){
  return b;
  var b = 7;  
}

foo(); //undefined
         

While creating Context of a function, JavaScript execution engine, scan through the whole function and brings the variable declaration to the top of the scope. Please note the value assignment will not be hoisted. Only the variable declaration is hoisted. And the variable gets the default value of unassigned, variable which is "undefined". Hence, you will get "undefined" returned as the value of "b".


function foo(){
  var b;
  return b;
  b = 7;
}
foo(); //undefined
         

Similarly if you assign a function to a variable (function expression) only the variable part will be hoisted. However if you have a function declaration, the full function will be hoisted.

Hoisting Process

You can google more about it or read spec or explore V8 code. And if you find any good materials, please let me know.

In the following code block, you have a function expression where the function is assigned to a variable "bar", a function declaration named "foo" and a variable "a".


function myFunc() {    
    var bar = function() {
            return 'world';
        };    
    function foo() {
        return 'fooed';
    }
    var a = 5;
}
         

The function declaration of the function foo will be hoisted to the top, followed by the variable bar (from function expression) and variable "a". Post-hoisting, the above mentioned code will look like-


function myFunc() {
    function foo() {
        return 'fooed';
    }    
    var bar;
    var a;    
    bar = function() {
            return 'world';
        };
    a = 5;        
}
         

combination of hoisting and scope

Question: what will you see in the console for the following example?


 var a = 1; 
  function b() { 
     a = 10; 
     return; 
     function a() {} 
  } 
 b(); 
 console.log(a);
              

Answer: 1

Explanation

  1. function declaration function a(){} is hoisted first and it behaves like var a = function () {};, hence in local scope a is created.
  2. If you have two variable with same name (one in global another in local), local variable always get precedence over global variable.
  3. When you set a = 10;, you are setting the local variable a , not the global one.

Hence, the value of global variable remain same and you get, 1 in the log. ref: js hoisting/scope

Take Away

Only the declaration of a variable is hoisted. The value assigned remains at its location. A function declaration is hoisted. If you have a function expression only the variable part of the function expression will be hoisted.

7. Closure

If you have a function within a function, execution of the inner function will create a scope inside of the outer function- a nested scope. Because the inside function scope is enclosed by the outer function scope, the inner function scope is called a closure (I invented this definition). Remember, to be a closure, you dont have to return a function, you just have to call a function inside a function.


function a(){
  function b(){
     console.log('closure');
  }
  b(); //creates closure
}
         

Closure Example:: Closure is a powerful feature of JavaScript. Let's have a look into the following example. We have a outer function called "counter" that has a local/private variable "i" and it returns a function (the returning function doesn't have a name and is known as the anonymous function). Inside the returning function (the inner function) returns the value of i and increases it by 1. Aha, very simple!


function counter(){
    var i = 0;
    
    return function(){
       return i++;
    }
}
         

Step-1: In the following code, we execute a counter function and assign the result of the counter to a variable "a". Hence, the value of "a" will be the inner function (returning function). Just remember the inner function returns the value of "i" and also increase the value of "i". Hence, when you execute the function "a", you will get the value of "i".

Step-2: Now if you execute function "a" (execute inner function), it will return 0 (value of i) and will increase it by 1. If you execute function "a" again it will return 1 (current value of i) and will increase the value of "i" by 1. Hence, in your subsequent call you will get 2, 3, 4, 5 ....

This means, closure holds the state of the local/private variable in the outer scope. And every time, you call it you get the value of "i" increased by 1. Have a look at Image-22 and try to understand it. If you don't get it the time around .... try, try again!


var a = counter();
a(); //0
a(); //1
a(); //2
a(); //3
a(); //4
a(); //5
         

Now you can have another variable "b" and assign the execution result of the function counter. If you execute "b" (the inner function of the counter), you will get 0. If you execute "b" again, you will get 1. followed by 2, 3, 4, ...


var b = counter();
b(); //0
b(); //1
b(); //2
b(); //3
         

This means, each closure maintains separate private variables of the outer function. This is a very powerful feature of JavaScript. Have a look at Image-23

Image-22: Scope inside Scope

Image-23: Separate Privacy

Image-24: Closure is Fun

Encapsulation:: You would not be able to access (get or set) the value of "i" from outside. Hence the privacy of the value of "i" is maintained secretly. This is called encapsulation in object oriented programming.

Closure Performance

Don't use a closure when you dont need a closure. And don't create too many nested scope. Why do more than you need? (Image-24)

Don't hold excessively large private memory. This could cause memory leak


//try to avoid this
function a() {
    var largeStr = new Array(1000000).join('x');
    return function () {
        return largeStr;
    };
}
         

Take Away

If you execute a function inside a function, a closure is created. It is not necessary to return a function in order to create a closure..

Closure saves the state of the outer function variable but does not expose those private variables. Now you can hide implementation detail while using closure. This is called encapsulation.

Don't mix up scope and context

Scope:: Scope determines the variable access of a function when it is executed. It depends on where and how a function is executed. Scope is unique to each execution.

Context:: Context depends on how the function is called. The laymen version of context is the value of "this". If you want to learn more, read Execution context and Stack in JavaScript or watch Understanding JavaScript this in a crystal clear way

Summary

Summary is just a collection of Take Aways from each section

references

JavaScript Identity resolution and Scope chain, Understanding Scope and Context, Variable scope explain, MDN: Functions and function scope

Express anger!

Disclaimer: I promised you will be able to explain to your grandma but i didn't promised that she will understand.

Feel free to express your anger (sorry folks, you have to use g+.). Also point out my mistakes ( technical, wrong answer, spelling, grammar, sentence..., whatever), let your dude learn and grow.