0

I presume the following instantiates, one by one, five instances of the fat arrow function object, one for each time around the loop.

for (const num of [1, 2, 3, 4, 5]) 
    (() => console.log(num))()

How does JavaScript associate these function instances with the for-loop block?

Was it necessary to modify the specification of loops like for, when block scoping was introduced in ES2015?

Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • 1
    I'm stuck on the verb "associate" – danh Feb 26 '20 at 00:14
  • 2
    Yes, it was necessary to modify the specification of for loops to introduce block scopes. But it was not necessary to modifiy the specification of functions, they always had been closures. – Bergi Feb 26 '20 at 00:16
  • 3
    This is a very broad question. Please clarify what exactly you are looking for, or I'll go close it as a duplicate of [Explanation of block scoping with for loops](https://stackoverflow.com/q/30899612/1048572) and [How do JavaScript closures work?](https://stackoverflow.com/q/111102/1048572) – Bergi Feb 26 '20 at 00:18
  • I thought there was some codification, in ES2015, of scoping of functions defined within blocks. I might have misremembered. Here we have an anonymous function defined within a block. If this was possible prior to ES2015, one difference would be that they can now be anonymous. The linked question explains that a per-block lexical environment is created for block-scoped declarations in the for-expression, and that values are copied between loops, but this question is about the body of the for loop. – Ben Aston Feb 26 '20 at 00:48
  • @Ben Yes, ES2015 introduced block scopes for proper (although [similar things were known before](https://stackoverflow.com/a/15035083/1048572)). But I don't really understand what your question is about, of course it was necessary to modify the specification to introduce the new feature. – Bergi Feb 26 '20 at 00:53
  • @Ben Also, if your question is only about the body and not the loop, why not reduce your example to `{ const num = 1; (() => console.log(num))(); }`? Also why do you care about the function being part of an IIFE? – Bergi Feb 26 '20 at 00:54
  • Basically: IIUC a single LE is created per iteration for use by the initialiser and increment expressions. But what about the body of the loop? Certainly the semantics are different because values are not copied between iterations. I guess the same LE is used, but without the copying? I also have a separate sub-question: how are anonymous declarations identified within an LE? – Ben Aston Feb 26 '20 at 00:55
  • 1
    @Ben Yes, lexically scoped variables in the loop body are getting declared in the lexical environment created by the iteration (which also holds the variables from the loop head). – Bergi Feb 26 '20 at 00:58
  • @Ben Regarding your sub-question, there are no anonymous declarations, so I don't understand what you are asking about. Function expressions are treated just like usual, and the close over the lexical scope they are created in. – Bergi Feb 26 '20 at 01:00
  • IIUC, a declaration (variable or function) gets an Environment Record, which is a name / value pair in the LE. In this example we have an anonymous function expression. Given that it has no name, how is it stored in the LE? – Ben Aston Feb 26 '20 at 01:04
  • 1
    @Ben It is an **expression**, not a declaration. It does not get a name, just like an expression like `1+1` does not introduce names in the LE. There is no variable binding associated to it. – Bergi Feb 26 '20 at 02:10

1 Answers1

0

In this case, this is creating an immediately invoked function expression (IIFE), and is available whether the variable is defined as a var, const, or let. This functionality predates ECMA.

The process is as follows:

  1. Populate num.
  2. Create the function referencing num.
  3. Execute the function referencing num.
  4. Repeat until the array runs out.

Where it makes a difference is when executing asynchronous code, using setTimeout, Promises, or async functions.


Michael Landis
  • 1,074
  • 7
  • 12