Closures are not magic
This page explains closures so that a programmer can understand them — using working JavaScript code. It is not for gurus or functional programmers.
Closures are not hard to understand once the core concept is grokked. However, they are impossible to understand by reading any academic papers or academically oriented information about them!
This article is intended for programmers with some programming experience in a mainstream language, and who can read the following JavaScript function:
function sayHello(name) {
var text = 'Hello ' + name;
var say = function() { console.log(text); }
say();
}
sayHello('Joe');
An example of a closure
Two one sentence summaries:
- A closure is one way of supporting first-class functions; it is an expression that can reference variables within its scope (when it was first declared), be assigned to a variable, be passed as an argument to a function, or be returned as a function result.
- Or, a closure is a stack frame which is allocated when a function starts its execution, and not freed after the function returns (as if a 'stack frame' were allocated on the heap rather than the stack!).
The following code returns a reference to a function:
function sayHello2(name) {
var text = 'Hello ' + name; // Local variable
var say = function() { console.log(text); }
return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"
Most JavaScript programmers will understand how a reference to a function is returned to a variable (
say2
) in the above code. If you don't, then you need to look at that before you can learn closures. A programmer using C would think of the function as returning a pointer to a function, and that the variables say
and say2
were each a pointer to a function.
There is a critical difference between a C pointer to a function and a JavaScript reference to a function. In JavaScript, you can think of a function reference variable as having both a pointer to a function as well as a hidden pointer to a closure.
The above code has a closure because the anonymous function
function() { console.log(text); }
is declared inside another function, sayHello2()
in this example. In JavaScript, if you use the function
keyword inside another function, you are creating a closure.
In C and most other common languages, after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed.
In JavaScript, if you declare a function within another function, then the local variables can remain accessible after returning from the function you called. This is demonstrated above, because we call the function
say2()
after we have returned from sayHello2()
. Notice that the code that we call references the variable text
, which was a local variable of the function sayHello2()
.function() { console.log(text); } // Output of say2.toString();
Looking at the output of
say2.toString()
, we can see that the code refers to the variable text
. The anonymous function can reference text
which holds the value 'Hello Bob'
because the local variables of sayHello2()
are kept in a closure.
The magic is that in JavaScript a function reference also has a secret reference to the closure it was created in — similar to how delegates are a method pointer plus a secret reference to an object.
More examples
For some reason, closures seem really hard to understand when you read about them, but when you see some examples it becomes clear how they work (it took me a while). I recommend working through the examples carefully until you understand how they work. If you start using closures without fully understanding how they work, you would soon create some very weird bugs!
Example 3
This example shows that the local variables are not copied — they are kept by reference. It is kind of like keeping a stack-frame in memory when the outer function exits!
function say667() {
// Local variable that ends up within closure
var num = 42;
var say = function() { console.log(num); }
num++;
return say;
}
var sayNumber = say667();
sayNumber(); // logs 43
JavaScript closures for beginners
Submitted by Morris on Tue, 2006-02-21 10:19. Community-edited since.
Closures are not magic
This page explains closures so that a programmer can understand them — using working JavaScript code. It is not for gurus or functional programmers.
Closures are not hard to understand once the core concept is grokked. However, they are impossible to understand by reading any academic papers or academically oriented information about them!
This article is intended for programmers with some programming experience in a mainstream language, and who can read the following JavaScript function:
function sayHello(name) {
var text = 'Hello ' + name;
var say = function() { console.log(text); }
say();
}
sayHello('Joe');
An example of a closure
Two one sentence summaries:
- A closure is one way of supporting first-class functions; it is an expression that can reference variables within its scope (when it was first declared), be assigned to a variable, be passed as an argument to a function, or be returned as a function result.
- Or, a closure is a stack frame which is allocated when a function starts its execution, and not freed after the function returns (as if a 'stack frame' were allocated on the heap rather than the stack!).
The following code returns a reference to a function:
function sayHello2(name) {
var text = 'Hello ' + name; // Local variable
var say = function() { console.log(text); }
return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"
Most JavaScript programmers will understand how a reference to a function is returned to a variable (
say2
) in the above code. If you don't, then you need to look at that before you can learn closures. A programmer using C would think of the function as returning a pointer to a function, and that the variables say
and say2
were each a pointer to a function.
There is a critical difference between a C pointer to a function and a JavaScript reference to a function. In JavaScript, you can think of a function reference variable as having both a pointer to a function as well as a hidden pointer to a closure.
The above code has a closure because the anonymous function
function() { console.log(text); }
is declared inside another function, sayHello2()
in this example. In JavaScript, if you use the function
keyword inside another function, you are creating a closure.
In C and most other common languages, after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed.
In JavaScript, if you declare a function within another function, then the local variables can remain accessible after returning from the function you called. This is demonstrated above, because we call the function
say2()
after we have returned from sayHello2()
. Notice that the code that we call references the variable text
, which was a local variable of the function sayHello2()
.function() { console.log(text); } // Output of say2.toString();
Looking at the output of
say2.toString()
, we can see that the code refers to the variable text
. The anonymous function can reference text
which holds the value 'Hello Bob'
because the local variables of sayHello2()
are kept in a closure.
The magic is that in JavaScript a function reference also has a secret reference to the closure it was created in — similar to how delegates are a method pointer plus a secret reference to an object.
More examples
For some reason, closures seem really hard to understand when you read about them, but when you see some examples it becomes clear how they work (it took me a while). I recommend working through the examples carefully until you understand how they work. If you start using closures without fully understanding how they work, you would soon create some very weird bugs!
Example 3
This example shows that the local variables are not copied — they are kept by reference. It is kind of like keeping a stack-frame in memory when the outer function exits!
function say667() {
// Local variable that ends up within closure
var num = 42;
var say = function() { console.log(num); }
num++;
return say;
}
var sayNumber = say667();
sayNumber(); // logs 43
Example 4
All three global functions have a common reference to the same closure because they are all declared within a single call to
setupSomeGlobals()
.var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 42;
// Store some references to functions as global variables
gLogNumber = function() { console.log(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}
setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5
var oldLog = gLogNumber;
setupSomeGlobals();
gLogNumber(); // 42
oldLog() // 5
No comments:
Post a Comment
Thanks for your comments