Block scope variables in 'For' and 'For-of' loops

// Prints
// Meow!
// I'm a talking cat!
// Callbacks are fun!

function go() {
   const messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];
   let msg = [];
   for (let i = 0; i < messages.length; i++) {
      msg.push(function () {
          print(messages[i]);
      });
   }
   for (let fct of msg ) {
      fct();
   }
}
go();

// Prints
// Callbacks are fun!
// Callbacks are fun!
// Callbacks are fun!

function go() {
   const messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];
   let msg = [];
   for (let message of messages) {
      msg.push(function () {
          print(message);
      });
   }
   for (let fct of msg ) {
      fct();
   }
}
go();

I learned that let makes variables block-scoped and solves the shortcoming of using var to declare variables. i.e let allows each iteration to capture a different value for the variable. This works with the traditional for loop as my first example above shows. However, I was surprised that if I use the for..of loop structure in ES6, the variable 'message' is not captured for each iteration in the function closure! Why is this?

2 answers

  • answered 2017-11-12 20:39 Gerard

    I think the issue was with how you're using the .push() method. Try the code below, it will console log your array correctly with either syntax.

    In this context, var and let would both have the same result. All the variables you declared will only have scope within the function they're declared in.

    function go() {
     const messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];
     let msg = [];
     for (let i = 0; i < messages.length; i++) {
        msg.push(messages[i]);
        };
    
     for (let fct of msg ) {
        console.log('for',fct);
     };
    };
    go();
    
    function go2() {
     const messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];
     let msg = [];
     for (let message of messages) {
        msg.push(message);
     };
     for (let fct of msg ) {
        console.log('for-of', fct);
     };
    };
    
    go2();
    

  • answered 2017-11-12 21:24 elouie99

    Turns out my JS shell is very outdated. When moving to the latest SpiderMonkey 45, it works as expected.