0

In the You Don't Know Javascript series, 1/3 of the way down IIFE's are described as not being closures themselves, but only if they're executed outside their lexical scope:

Chapter 3 introduced the IIFE pattern. While it is often said that IIFE (alone) is an example of observed closure, I would somewhat disagree, by our definition above.

This code "works", but it's not strictly an observation of closure. Why? Because the function (which we named "IIFE" here) is not executed outside its lexical scope. It's still invoked right there in the same scope as it was declared (the enclosing/global scope that also holds a). a is found via normal lexical scope look-up, not really via closure.

var a = 2;

(function IIFE(){ // not actually a "closure"
    console.log( a ); 
})();

In this SO post, the following snippet was given as an example of a closure:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

I am trying to understand this in terms of the definition of closure (as defined in this medium article):

To use a closure, simply define a function inside another function and expose it. To expose a function, return it or pass it to another function. ...

The inner function will have access to the variables in the outer function scope, even after the outer function has returned.

I understand that a closure is a "stateful function", and that it is

a way to "remember" and continue to access a function's scope (its variables) even once the function has finished running.

So in this example, I see that the loop's i is remembered when passed into the closing IIFE.

My question is:

Where is the "passing to another function or returning" portion happening? My guess is that the IIFE is able to remember the outer i for loop value at each iteration because the IIFE is passed to the window?

Basically, my understanding is that a closure is defined as remembering an outer scope's value after the Garbage collector cleans that outer scope up, and that the usage of it is to expose the closure by returning it and accessing it outside its lexical scope. Is this correct? So where is that "accessing it outside the lexical scope" happening?

user3871
  • 12,432
  • 33
  • 128
  • 268
  • A better example of an IIFE that's a closure would be `let plus3 = (x => y => x + y)(3);`. Here the variable `x` is closed over. When you call it with the value 3 it returns a function that takes another argument y and adds 3 to it. – Jared Smith Jul 17 '17 at 18:42
  • I'm not sure a closure becomes not-a-closure simply based on when/where it is invoked... – Oliver Charlesworth Jul 17 '17 at 18:42
  • @OliverCharlesworth I think the author the OP is quoting meant something to the effect of "when it doesn't actually close over anything of interest in the surrounding scope". – Jared Smith Jul 17 '17 at 18:43
  • @growler the `window` has nothing to do with it, all the anonymous functions passed to `setTimeout` are closing over the variable `i` in the loop. – Jared Smith Jul 17 '17 at 18:47
  • Possible duplicate of [How do JavaScript closures work?](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Jared Smith Jul 17 '17 at 18:50
  • @JaredSmith yes I understand that. But the question is, if the anonymous functions passed to `setTimeout` close over the variable `i`, why isn't that `i` preserved? The wrapping IIFE (`(function (i) { ... })(i)`) creates more closured scope which somehow preserves the `i` after the loop finishes. Is that because the wrapping `IIFE` is executed at a different scope? – user3871 Jul 17 '17 at 18:54
  • 1
    In the `(function (i) { ... })(i)` example, the purpose of that IIFE is to create a local (argument) variable so that each of the `setTimeout` callbacks refer to a different, local variable named `i` instead of the same global `i`. The function passed into `setTimeout` clsoes over the `i` created by the argument named `i` in the surrounding IIFE. – apsillers Jul 17 '17 at 18:58
  • @growler `i` *is* preserved. It *has to be*, because the `setTimeout` makes the execution asynchronous. Without the IIFE to create the closure, `i` will be whatever the value is when the callback is actually called, in this case it will be `someVar.length - 1` over and over again rather than incrementing values. Punch `for (var i=0; i<10; ++i) setTimeout(function() { console.log(i) })` in to the console. – Jared Smith Jul 17 '17 at 18:59
  • @apsillers I think I've got it. Since it's an anonymous function, it's placed outside the event loop until the `for loop` finishes (at which point, `i` === `someVar.length-1`). When the event loop stack is clear, the `setTimeout` is executed and returns... without the `IIFE`, `setTimeout`'s anonymous function is still closing over `i`, but it's already after the event loop has finished. The `IIFE` is called for each iteration of `i` and creates a local variable of `i`'s value. – user3871 Jul 17 '17 at 19:11
  • So with or without the `IIFE`, the `setTimeout`'s anonymous function still closes over `i`. We just want to close over the correct local `i` from the executed `IIFE` – user3871 Jul 17 '17 at 19:11
  • @Growler yes that is correct. Sorry if I was unable to communicate that clearly, glad apsillers got it for you. – Jared Smith Jul 17 '17 at 20:04
  • @JaredSmith makes perfect sense. Thank you! – user3871 Jul 17 '17 at 20:25

1 Answers1

3

in this example, I see that the loop's i is remembered when passed into the closing IIFE

No. The IIFE is only providing the scope for the i value to be remembered. As the quotes you cited state, the IIFE function is not a closure. The function expression that uses the i is the closure:

(function IIFE(i) {
    // this is the scope of i

    window.setTimeout(function closure() {
        // this function closes over i
        alert("Value of my local 'i' is still "+i+" even after 10 seconds");
    }, 10000);
})(0);
// ^ this is some arbitrary value passed to the IIFE - it could have been a loop variable

Where is the "passing to another function or returning" portion happening?

It's the closure function being passed into setTimeout, which will call it back from a place where i would usually be no longer defined - if it wasn't a closure.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375