Iteration scoped for-loop variables with keyword 'let'

Question | Feb 6, 2016 | hkumar 

enter image description here

[Recommended Reading]
Javascript let keyword

Pre-ES6 Javascript does not have block scoped variables. A block is any code enclosed in braces {..}. The smallest possible scope in pre-ES6 Javascript is a function. All variable declarations anywhere within a function are hoisted to top of the function. There is an interesting blog post on this subject: No Block Scope In Javascript.

Here is one of many pitfalls associated with having no block scopes - creating closures in a for loop:

var tasks = [ function() { console.log('Task One'); },
              function() { console.log('Task Two'); }, 
              function() { console.log('Task Three'); } ];

for(var t=0; t < tasks.length; ++t) {
      setTimeout( function() { tasks[t](); }, 1000*(t+1) );
 }

Above code would result in Uncaught TypeError: tasks[t] is not a function 3 times at 1 second interval. What is going on there? Each closure callback passed to setTimeout() is referencing same instance of t. By the time all callbacks execute - after the for loop ends - the value of index t is 3, which is out of bounds of array tasks.

A commonly used way to get around above problem is to pass the loop variable as argument to an Immediately Invoked Function Expression (IIFE) to create a new function scope:

for(var t=0; t < tasks.length; ++t) {
      (function ( t ) {
          setTimeout( function() { tasks[t](); }, 1000*(t+1) );
      })( t );
 }

If you have a choice to use ES6, you don't have to outflank the problem by creating an IIFE scope. Instead you can simply replace var t in original code by let t. ES6 keyword let is used for declaring block scope variables. Also, a for loop variable - e.g t in above code - declared with let is scoped per iteration, that means for every iteration of loop the variable is bound again.

To drive this point home, here is another flavor of similar problem solved with let. Look at it and try to answer the following question:

var funcs = [  ];
for( let i=0; i < 3; ++i ) {
   funcs.push( function () { console.log( i ? true : false ); } );
}

funcs[ 0 ]( );  // ?

What would be logged on console?