I understand that closures keep the execution context alive by saving a reference to the executed function.
I'm wondering whether the whole context is saved or only the required parts.
In the former case I need to structure the functions in a way that no memory is wasted. This should be the design goal anyway, but I would like to know if JavaScript takes care of it, too.
Here's a simple example (on the basis of Single Page Web Applications, Mikowski/Powell, p. 56):
var ctx;
var outer_function = function () {
var dummy = 'not required for output';
var output = 'output';
var inner_function = function () {
return { output: output };
}
return inner_function;
};
ctx = outer_function();
// returns { output: 'output' }
ctx();
Is the dummy
object stored in the closure after the execution of
outer_function
even though it is not accessible and won't be used?
Disadvantages of closures There are two main disadvantages of overusing closures: The variables declared inside a closure are not garbage collected. Too many closures can slow down your application. This is actually caused by duplication of code in the memory.
Closures are useful because they let you associate data (the lexical environment) with a function that operates on that data. This has obvious parallels to object-oriented programming, where objects allow you to associate data (the object's properties) with one or more methods.
Closures have to do with how javascript is scoped. To say it another way, because of the scoping choices (i.e. lexical scoping) the javascript designers made, closures are possible. The advantage of closures in javascript is that it allows you to bind a variable to an execution context.
Closures are frequently used in JavaScript for object data privacy, in event handlers and callback functions, and in partial applications, currying, and other functional programming patterns.
We can see that Chrome eliminates unused variables (unless there is a direct eval
call). Consider the following code (here's a fiddle to follow along with):
var bar = function() {
var hello = "world";
var unused = "nope";
return function(s) { console.log(hello); debugger; return s; };
}
var g = bar();
g(1);
bar
returns a function that has access to the inner variables hello
and unused
. The variable hello
is used inside of the returned function, but unused
is not. unused
is entirely inaccessible after the termination of bar
.
When we run this code with Dev Tools already open (to break on the debugger
statement), we see:
We see that only hello
has survived in the Closure scope. unused
has disappeared. We can confirm this by going to the console (while the code is still paused) and seeing that hello
is defined but accessing unused
produces a ReferenceError.
This is the fundamental principle of garbage collection in action: if a variable becomes completely inaccessible, it should be freed. No one specified that JavaScript engines must free completely inaccessible variables, but it's an obvious performance win that is indistinguishable (in language-completeness terms) from not garbage collecting unused variables.
However, direct calls to eval
can access otherwise inaccessible variables. If we modify our returned function to include a direct call to eval
...
var foo = function() {
var hello = "world";
var unused = "nope";
return function(s) { console.log(hello); debugger; return eval(s) };
}
var f = foo();
f(1);
We see that all local Closure-scope variables are now preserved:
This is because the environment cannot safely garbage collect any local variables, e.g., for fear that eval(s)
might evaluate to unused
.
You might wonder: how can the engine reliably detect the existence of eval
calls? Couldn't you do something like window["ev"+"al"](s)
instead, in a way that the engine can't reliable detect? The answer is that such an "indirect" eval
call does not have access to closure-scope variables and executes in the global scope. Only direct calls that use the identifier eval
as part of a function call can access local variables, and that's easy to detect.
If you're interested in learning more about direct eval
calls, see my answer on global.eval is not able to visit variables in the lexical scope.
Incidentally, this is one of the main performance-based reasons why "eval
is evil". The presence of a single direct call to eval
in your code prevents the entire closure from being garbage-collected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With