Closures in Loops; Be Careful Enough [RE#2]

Closures could sometimes be tricky to the point that you may find out things too late/costly.

Look at this code fragment:

let a = [1, 2, 3];
let fn = [];
for (x of a) fn.push(() => x);
for (f of fn) console.log(f());

Simply, we've added three functions to fn that each should return their corresponding value of list a. So, we expect to see this as output:

0
1
2

But it's not what's going to happen. If you run the code you'll end up seeing this absurd thing:

3
3
3

Now, you can guess what has happened. The variable x is not even evaluated at the time of running the first loop. This is actually the correct behaviour, since we just declared a function that maybe called later, but certainly not now. The three functions are all ready to read the content of x whenever they've been called, and at that time x was ended up being 3.

This is quite common to have such setup in which you're using the loop's variable inside an anonymous function. To make sure you don't fall in, pass the value of the loop variable to your function. That is:

let a = [1, 2, 3];
let fn = [];
for (x of a)
  fn.push(
    (function (v) {
      return () => v;
    })(x)
  );
for (f of fn) console.log(f());

Here, I've used both syntaxes of anonymous function definitions to make things clean/separate. I'm sure you got the point, but in case you're interested to know more, have a look on these great posts:


About Regular Encounters
I've decided to record my daily encounters with professional issues on a somewhat regular basis. Not all of them are equally important/unique/intricate, but are indeed practical, real, and of course, textually minimal.