Sunday, November 17, 2013

JavaScript : Closures

You may also like to see:

Here I will try to explain concept of Closures, with working JavaScript code.

Closures can simply be defines as inner function in JavaScript has the access to all variable define in outer functions.

Here is a simple code with Closures:

function sayHelloToClosures(yourName) {
  var text = 'Hello Closures from' + yourName;
  var sayAlert = function() { alert(text); }
  sayAlert();
}

Here in this example inner function sayAlert() has access to text defined in outer function sayHelloToClosures(), yes that simple concept is Closures.

Closures says the local variables for a function is not de-allocated after function has returned.

Lets see it another way:

function outerFunction(x) {
  var z = 3;
  return function (y) {
    alert(x + y + z);
  }
}
var innerFunction = outerFunction(2); // innerFunction is now a closures.
innerFunction(10);

Here when we call outerFunction() it set the values x = 2 and z = 3 and return a function which takes a parameter y and sum of x, y and z. This function is assigned to innerFunction() on return. In general the variables x and z should be destroyed as outerFunction() has completed its execution and it returned successfully, but closures say it exists for innerFunction() and whenever we call innerFunction() values of x and z will always be the same defined by the outerFunction().

so whenever we call innerFunction() it will add x + z = 5 to y and alert the value.

So when a function is invoked in JavaScript , it create a new execution context. This context has access to Parent objects with the arguments for current function get invoked, this execution context also has access to the variables declared outside of its scope.

The simplest example:

var foo = 20;
function myFunction(x) {
  var bar = 6;
  console.log(foo); // will output 20
  console.log(bar); // will output 6
  console.log(x); // will output 10
}
myFunction(10);

As we know JavaScript always uses the Object by reference when it passes as argument, so if we pass object to a function as closures and call innerFunction() which updates the value of object, on next execution of innerFunction() it will have updated value, not the value set by the outerFunction().

Let see the example:

function outerFunction(x) {
  var z = 3;
  return function (y) {
    x=x+1;//x++
    alert(x + y + z);
  }
}
var myVal = new Number(2);
var innerFunction = outerFunction(myVal); // innerFunction is now a closures.
innerFunction(10); //will alert 16
innerFunction(10); //will alert 17
innerFunction(10); //will alert 18


On first execution it will get the Number Object and increment it and set to closures function, but here it also updated the value of object as object in JavaScript passed by reference, so updating its value will update the original object, so next time on calling innerFunction() it will get incremented value and so on. So on each time function will get incremented value.

Loops and Closures


How closures can cause issues in loops and how to solve it?

As closure tells you that innerFunction() use the outer or upper scope variables copy(if it is not object), so it may cause some ambiguous functionality in loops, like:

var myFunctions = {};
for (var i = 0; i < 3; i++) {      // let's create 3 functions
    myFunctions[i] = function() {  // and store them in myFunctions
        console.log("My value: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    myFunctions[j]();              // and now let's run each one to see
}
It will output:
My value: 3
My value: 3
My value: 3
instead of:
My value: 1
My value: 2
My value: 3

why?

because, your myFunctions() is bounded to outer-scope variable i which is changed in each loop so after complete loops it value is 3, that is the reason it is printing 3 each time. So, How to solve it? Pass variable i as a parameter, instead of using it directly, as we know if we pass a parameter function makes its own local copy of that variable (if it is not object type which pass by reference). So each time function has its own local copy of variable which not get updated by loop iteration. Here is solution for above issue:
var myFunctions= [];

function createMyFunction(i) {
    return function() { 
           console.log("My value: " + i); 
    };
}

for (var i = 0; i < 3; i++) {
    myFunctions[i] = createMyFunction(i);
}

for (var j = 0; j < 3; j++) {
    myFunctions[j]();    // and now let's run each one to see
}
Now as each time creatMyFunction() will have its own copy so it output the correct result, as expected:
My value: 1
My value: 2
My value: 3
You may also like to see:

2 comments:

  1. In your code:

    console.log(bar); // will output 35

    I alert'ed this and the output is 6, not 35. How do you log 35?

    ReplyDelete
    Replies
    1. Yes, it will be 6!!! updated post accordingly. thanks :)

      Delete