JavaScript ES6: for - for-in - for-of

Let & const in Block level binding in Loop compare to var | Understanding ES6

In JavaScript, Developers wanted block level scoping of variables only within for loop, where the temporary incremental variable is only to be used inside the loop.

As we know from my previous post, that when we declare a variable with var, it will be hoisted to the top of the function. So even if we declare the variable within a for loop, we can access it from outside of the loop as well.

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

// we can still use i, outside the for loop
console.log(i);

In other languages where Block level is default scope, the i variable will be accessible only in the for loop and will not be accessible from outside. So to fix this in JavaScript, ES6 has introduced the let & const datatypes.

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

// i is not accessible outside of the loop and will throw an error
console.log(i);

In above example, i will be accessible only in the for loop and will throws an error once the code execution goes out of the loop. The same goes for the variables declared with const as well.

Functions in Loop

Until now while declaring function in the for loop was troublesome due to the characteristics of the var, as it will hoist the variable to the top. Let’s see below example:

var arryFuncs = [];

for(var i = 0; i < 5; i++) {
   arryFuncs.push(function() {
      console.log(i);
   });
}

arryFuncs.forEach(function(valFunc) {
   valFunc();      // this will output number 4 every time.
});

In the above example, you might think that the valFunc() will return numbers form 0 to 4, but in fact it will return ‘4’ five times, it’s because the variable i is hoisted variable and it will be available even after the loop. So when code execute the for loop it will re-assign the value of i till 4 and the latest value of the i will be 4. So when we iterate through the arryFuncs() and call the function, it will print out 4 each time.

To fix this situation we can use the Immediately Invokable Function and re-write the above code as:

var arryFuncs = [];

for (var i = 0; i < 5; i++) {
   arryFuncs.push(function(val) {
      return function() {
         console.log(val);
      }
   }(i));
}

arryFuncs.forEach(function(valFunc) {
   valFunc();      // this will now output - 0, 1, 2, 3, 4
});

In above code, I have returned a function inside immediately invokable function and passed the value of i. So immediately invokable function will create it’s own copy of variable i and stores it and then pass it on to the function inside which will return the value as expected.

Fortunately with ECMAScript 6, Block level binding will help in this kind of situation. Introduction of new let & const datatype will solve this situation and simplifies the code. Please read my earlier blog to better understand the let and const datatypes.

let with Loops

In the above code, where we used var and with immediately invokable function. If we declare the variable with let we can simplify the code by omitting the immediately invokable function as below:

var arryFuncs = [];

for(let i = 0; i < 5; i++) {
   arryFuncs.push(function() {
      console.log(i);
   });
}

arryFuncs.forEach(function(valFunc) {
   valFunc();      // output - 0, 1, 2, 3, 4.
});

The above code will output correctly as the variable declared with let is only accessible inside the loop or block scope, so every time in the for loop, i will be created again and it will have new value assign from the iteration which will then passed to the function written inside the loop. The same method we can also use in for-in or for-of loop as well.

var arryFuncs = [],
   valObject = {
      x: "Hello",
      y: "World",
      z: "Good Morning"
   };

for (let k in valObject) {
   arryFuncs.push(function() {
      console.log( valObject[k] );
   });
}

arryFuncs.forEach(function(valFunc) {
   valFunc();  // this will output - "Hello", "World" and "Good Morning"
});

In above example, the for-in loop works same as the for loop, the value of k will be created every time hence each function will have it’s own copy of the variable.

const with Loops

The const variables needs to be initialised at the time of declaration as ECMAScript 6 does not allow to change the value of the const variables. So in case if we use the const variable in the for loop, it will work as expected in the first iteration but it will throw an error in the second iteration as ES6 will not allow to change the value of the const variable.

var arryFuncs = [];

// this will throw an error on second iteration
for(const i = 0; i < 5; i++) {
   arryFuncs.push(function() {
      console.log(i);
   });
}

arryFuncs.forEach(function(valFunc) {
   valFunc();
});

But we can use the const variables in the for-in or for-of loop and they will work as expected. The only condition is that you can not change the value of the variables.

var arryFuncs = [],
   valObject = {
      x: "Hello",
      y: "World",
      z: "Good Morning"
   };

for (const k in valObject) {
   arryFuncs.push(function() {
      console.log( valObject[k] );
   });
}

arryFuncs.forEach(function(valFunc) {
   valFunc();  // this will output - "Hello", "World" and "Good Morning"
});

As we are not changing the value of the k in the above example. It will executes perfectly because the const variable will be created through out the loop and will have it’s own copy and binding.

Result might have been different if we use var to declare variable instead of let & const in the for-in loop example. As we now know that the variable declared with var will be accessible outside of the loop and it’s value will be the last value of the loop iteration. So in that case the out of each function will be Good Morning.

Web/ UI & Front-end developer based in Ahmedabad, GJ, India. Here to help/ discuss community to spread web awareness.

Leave a reply:

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.