7

Although i understand that let allows you to declare variables that are limited in scope to the block, I encountered a strange difference between let and var while using it with javascript closures. Here is my example:

With let:

function buildFunction() {

  var arr = [];
  for(var i = 0; i < 3; i++) {
    let j = i;  //Using let to assign j
    arr.push(
      function(){
        console.log(j);
      }
    )
  }
   return arr;
}

var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();

The above code snippet outputs:

0
1
2

With var:

function buildFunction() {

  var arr = [];
  for(var i = 0; i < 3; i++) {
    var j = i;  //Using var to assign j
    arr.push(
      function(){
        console.log(j);
      }
    )
  }
   return arr;
}

var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();

The above code snippet outputs the following:

2
2
2

My question is:

  1. If U am using var inside a block and assigning it a value during execution, shouldn't it work exactly like let and store different copies of j in memory ?

  2. Does javascript handle let and var differently within a closure ?

Any clarifications on this would be highly appreciated.

Xiddoc
  • 3,369
  • 3
  • 11
  • 37
g.005
  • 396
  • 1
  • 12
  • @oen44: No this is not a duplicate. Please read the question carefully – g.005 May 07 '17 at 17:55
  • I think it answers "*if i am using var inside a block, shouldn't it work exactly like let?*" perfectly fine. The answer to the second question (*No*) becomes irrelevant then. – Bergi May 07 '17 at 18:35

1 Answers1

12

var scopes to the function; let scopes to a block of code.

In your example j is scoped to the function buildFunction() when you use var. This means your using the 'same' j in every function. When your for loop runs j is set to 0, then 1, then 2. When you then run the console logs your referencing the j that was set to 2, so you get 2 2 2.

When you use let, you scope j to that iteration of the for loop. This means every iteration has a 'different' j, and the console logs print what you expect them to print.

If you were using ES5 and needed to use var, you could replicate the let effect (have it print 0 1 2), by wrapping your original anonymous function with a self-executing function and passed j in as an argument. This would create a new scope for j in the self-executing function whose value is the value of j in the current iteration.

function buildFunction() {

  var arr = [];
  for(var i = 0; i < 3; i++) {
    var j = i;  //Using var to assign j
    arr.push(
      //self executing function
      (function(j) { //j here is scoped to the self executing function and has the value of j when it was called in the loop. Any changes to j here will not affect the j scope outside this function, and any changes to j outside this function will not affect the j scoped inside this function.
        //original function
        return function() {
          console.log(j);
        }
      })(j) //call with the value of j in this iteration
    )
  }

  return arr;
}

var fs = buildFunction();
fs[0]();
fs[1]();
fs[2]();
Nick Freitas
  • 328
  • 2
  • 8