1

I have a question related to JavaScript Closures, in specific to the scope when calling a (inner) function.
The question also relates to browser memory leaking as we will see.

At first a "short" introduction to my problem:

So I know when a function is called it enters an execution context (which is added on top of the call stack). There an "Activation object" is created with all the local variables, the function's arguments object and parameters added to it (as properties).

Since inner functions can access variables of enclosing functions - even if the inner function is called after the execution of its enclosing function has ended - there has to be some reference to the "free" (= outer) variables. This is managed by the scope chain which is also added to the execution context.

According to this site, the execution context's scope consists of the actual Activation object at the top of a list which is hiddenly saved on the function itself:

A scope consists of a list (or chain) of objects. Each function object has an internal [[scope]] property [...] The scope that is assigned to the execution context of a function call consists of the list referred to by the [[scope]] property of the corresponding function object with the Activation object added at the front of the chain (or the top of the list).

This is completely logic because due to this internal scope property, references to the free variables exist and so firstly the Closure is able to reach the variables (by going through the scope chain) and secondly it prevents the Garbage Collector from freeing the associated memory when the execution of the enclosing function ends.

In short: the scope chain (including the function's internal scope property) is the reason why Closures work as they do.


So now concretely to my question:

Which variables are added to the function's internal scope property?

As i understand the following part of this MDN article:

A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time that the closure was created.

... this would mean that references to all local variables would be added to the internal scope property when forming a Closure.

This seems a bit unnecessary, when the Closure doesn't use all the local variables from its enclosing function, but no matter; the real problem is that this would lead to memory leaks as we will see in the following.

Consider following code (it's a small reduction of this article, to which we will come to later):

var theThing = null;
var replaceThing = function () {
  var priorThing = theThing;                // hold on to the prior thing

  theThing = {
    longStr: new Array(1000000).join('*'),  // create a 1MB object
    someMethod: function () {
      console.log("someMessage");
    }
  };
};
setInterval(replaceThing, 1000);            // invoke `replaceThing' once every second

After the first function call of replaceThing following would happen:

  • priorThing = null
  • theThing references to a new object (let's say with address 0x01) containing following properties:
    • a 1MB object
    • function someMethod whose internal scope property has references to all local variables of the enclosing function, this means also to priorThing which is null

After the second function call:

  • priorThing = reference to object with address 0x01
  • theThing references to a new object (let's say with address 0x02) containing following properties:
    • a 1MB object
    • function someMethod whose internal scope property has a reference to priorThing (with the address 0x01)
      • priorThing itself points to a object with the same structure ...

So what's happening?

On each function call a new object is created which references to the object created in the function call before; although we don't even use this object in our new object. It just happens due to the scope chain of the function closure.

So we see saving references to all local variables of the enclosing function is pretty bad.

A better way would be to just save these we really need.

So, from the previous article I conclude that this is really done so and this would solve the memory leak problem I described above.

But furthermore it says following:

But as soon as a variable is used by any closure, it ends up in the lexical environment shared by all closures in that scope.

Well, this would create another possibility to create memory leaks as the author describes in his example - but this would be a programmer's bug.


Okey, so now summed up shortly:

  • Are all local variables referenced in the closure's scope? (as MDN says)
    • Would be the worst, because of memory leaks.
  • Are the variables referenced by all closures if any closure uses it? (as this source says)
    • Would be the best, because memory leaks are avoided - but bugs could happen, if the programmer isn't aware of this behaviour.
  • Or does every closure have its own references depending on which variables are used by themselves?
    • Would completely avoid memory leaks, but a bit more memory would be needed because there has to be a own "lexical environment" object (which is referenced by the internal scope property) for each closure.
andiluk
  • 46
  • 5
  • 1
    "*Are all local variables referenced in the closure's scope?*" - Yes, that's what the spec says - because it doesn't concern itself with garbage collection. Also this happens when you use `eval` in the closure, which could potentially reference the variables. "*Are the variables referenced by all closures if any closure uses it?*" - Yes, that's what some implementations do as an optimisation, but no it does not cause bugs as it's completely transparent. "*Or does every closure have its own references depending on which variables are used by themselves?*" - no, the closed-over scope is shared. – Bergi Sep 01 '17 at 22:32

0 Answers0