-2

I am trying to understand closures in depth. Consider this example from w3schools:

var add = (function outerAdd() {
  var counter = 0;
  return function innerAdd() {
    counter += 1;
    return counter
  }
})();

console.log(add());
console.log(add());
console.log(add());

// the counter is now 3

The page says that 'Local variables have short lives. They are created when the function is invoked, and deleted when the function is finished.' This means that counter is deleted after the outer self invoking function outerAdd runs.

But the inner returned function innerAdd is still able to access counter because of the scope chain that was formed when innerAdd was defined (or executed?). So now the question is, does the scope chain copy the variable values when a new execution context is created? Because if the scope chain simply maintained a pointer to counter, it should throw an error as counter got deleted after outerAdd function finished running.

Also, what if counter was an object? How will the 'copying' behaviour work then? As objects are copied by reference in JavaScript.

EDIT: I appreciate all the answers here. But what I am really trying to understand is the inner working of closures, as explained by execution contexts and scope chains. So I am looking for an explanation which is something on those lines. Other Stackoverflow answers don't really explain how this happens internally?

Like I don't understand when exactly was counter referred by the scope chain of innerAdd? If it was during execution of innerAdd (i.e. when an execution context was formed for innerAdd), then why was counter not garbage collected at that time? As once outerAdd finished execution, there is nothing referencing counter.

darKnight
  • 5,651
  • 13
  • 47
  • 87
  • Variables are not deleted when the function finishes, they are deleted when they are no longer needed. If they are still referenced by a closure, they are not deleted. – Bergi Jul 12 '19 at 11:02

2 Answers2

0

The page says

Don't use W3Schools. Really. It is a terrible site with very poor explanations of just about everything.


A variable exists until nothing references it any more.

counter is referenced by innerAdd so counter will exist until innerAdd no longer exists. Since innerAdd is returned from outerAdd and assigned to add, it continues to exist until (in your example) the end of the program is reached.

what if counter was an object?

counter is a variable. The value of it in your example is a number. If the value was a reference to an object, then it would work in exactly the same way.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • But I still don't understand when exactly was `counter` referred by the scope chain of `innerAdd`? If it was during execution of `innerAdd` (i.e. when an execution context was formed for `innerAdd`), then why was `counter` still not garbage collected at that time? As once `outerAdd` finished execution, there is nothing referencing `counter`. – darKnight Jul 12 '19 at 11:03
  • "But I still don't understand when exactly was counter referred by the scope chain of innerAdd?" - `counter += 1;` and `return counter` – Quentin Jul 12 '19 at 12:24
  • "If it was during execution of innerAdd (i.e. when an execution context was formed for innerAdd), then why was counter still not garbage collected at that time?" — Because the value assigned to `add` (a function) still exists and continues to use that variable. – Quentin Jul 12 '19 at 12:25
  • "As once outerAdd finished execution, there is nothing referencing counter" — As I said in the answer, `innerAdd` references `counter` – Quentin Jul 12 '19 at 12:25
0

w3schools is, as usual, not very accurate. It was trying to use that description in the context of

function add() {
  var counter = 0; 
  counter += 1;
}

where counter is a local variable, and also, once an invocation of add completes, since nothing else can possibly reference that inner counter variable anymore, it will get garbage collected soon. But just because a variable is local does not mean it's not referencable anymore / gets deleted, as the code in your question shows. A variable is only garbage collected (deleted from memory) once nothing else can reference it, for the most part.

The value is not copied when your innerAdd function runs - rather, the old counter variable in scope of the IIFE simply continues to exist, because invocations of innerAdd will still be able to view the counter variable, so the counter variable doesn't get GC'd.

Also, what if counter was an object? How will the 'copying' behaviour work then? As objects are copied by reference in JavaScript.

Closures and garbage collection work the same way no matter whether the variable is a primitive or an object, If, for example, counter was an array instead, which was pushed to on each invocation, the same thing would happen:

(() => {
  var add = (function outerAdd() {
    var arr = [];
    return function innerAdd(item) {
      arr.push(item);
    }
  })();

  // here, the "arr" variable will still exist because it can be seen by the `add` function
  add(5);
  add(3);
  add(5);
})();
// here, since the `add` function is no longer referencable, and nothing else can reference the `arr`,
// the `arr` will get garbage collected

It would remain in memory as long as something can reference it.

For differences between objects and primitives, see Is JavaScript a pass-by-reference or pass-by-value language?.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320