A closure is a function which has captured ("closed over") one or more of the variables that were in scope when the function was defined.
There are several similar ways to implement closures. This is best understood by example. I will use pseudocode here.
In some languages, you have to explicitly state which variables you want to capture.
var x = "chips" var more_spam = function () capture (x) { x = x + " and spam" print(x) } more_spam() // "chips and spam" more_spam() // "chips and spam" x = "bacon" more_spam() // "chips and spam"
Notice how every invocation of more_spam
begins with a variable x
available, whose value is always "chips"
at first. This is because we have captured x
's value.
Meanwhile, the original x
can be reassigned freely.
In a programming language which captures by reference, the same pseudocode yields different results.
var x = "chips" var more_spam = function () capture (x) { x = x + " and spam" print(x) } more_spam() // "chips and spam" more_spam() // "chips and spam and spam" x = "bacon" more_spam() // "bacon and spam"
Here, more_spam
's x
and the original x
are the same variable; modifying one modifies the other. This is because we have captured x
by reference.
Interestingly in this case, it's possible for x
to drop out of scope even though more_spam
hasn't and is still callable. When you call more_spam()
, you will get the correct behaviour, because x
still exists with its correct value, even though it is no longer directly accessible.
Some languages allow you to select whether you capture by reference or by value, depending on which syntax you use when you declare the function.
In some languages, the explicit capturing syntax doesn't exist. You just write:
var x = "chips" var more_spam = function () { x = x + " and spam" print(x) } x = "bacon" more_spam() // "chips and spam" if capturing by value // "bacon and spam" if capturing by reference
Here, more_spam
captures x
automatically, without being told. In fact, more_spam
captures every available lexical variable, of which there could be many more beside x
!
This can sometimes be problematic. If you want to use a variable named x
inside your function, but you don't want to capture the x
from outside it, you must remember to declare a brand new variable named x
inside your function:
var x = "chips" var more_spam = function () { var x = "more spam" print(x) } x = "bacon" more_spam() // "more spam" every time
Use caution when creating closures in a loop. If you capture the loop variable by reference, watch what happens:
var more_food = {} for var food in ["eggs", "chips", "spam"] { more_food[food] = function() { print("more " + food) } } more_food["eggs"]() // "more spam" more_food["chips"]() // "more spam" more_food["spam"]() // "more spam"
Here, all the closures have captured references to the same loop variable, food
. After the loop is over, the value of this variable is "spam"
, no matter how it is accessed.
This gotcha is most often seen in JavaScript, but also appears in Python.
Discussion (25)
2014-02-08 12:30:07 by qntm:
2014-02-08 13:46:20 by hpc:
2014-02-09 03:39:15 by MHD:
2014-02-09 10:39:28 by anonymouse:
2014-02-11 17:42:02 by Redsplinter:
2014-02-11 17:43:37 by Readplinter:
2014-02-12 14:54:31 by skztr:
2014-02-12 15:05:52 by Moti:
2014-02-13 13:28:54 by Connor:
2014-02-16 18:22:52 by qntm:
2014-02-18 23:01:03 by Dentin:
2014-02-20 18:54:17 by Anon:
2014-02-20 18:56:07 by Anon:
2014-02-20 20:19:09 by qntm:
2014-02-20 22:09:55 by Dentin:
2014-02-20 23:16:35 by Aegeus:
2014-02-24 20:26:07 by Anon:
2014-02-26 11:50:27 by Veky:
2014-02-26 11:52:00 by frymaster:
2014-02-26 21:49:48 by qntm:
2014-03-11 17:18:00 by testme:
2014-03-11 19:15:47 by qntm:
2014-06-16 17:36:54 by Pipoca:
2014-07-04 10:51:52 by Coda:
2015-05-24 21:32:56 by anonymous: