Closures are functions that refer to variables that are used locally, but defined in an enclosing scope. In other words, these functions ‘remember’ the environment in which they were created.
The inner function has access not only to the outer function’s variables, but also to the outer function’s parameters. Note that the inner function cannot call the outer function’s arguments, even though it can call the outer function’s parameters directly.
I will lay out a couple examples of closure starting with simple examples moving on to a little more confusing.
The variable add is assigned the return value of a self-invoking function.
The self-invoking function only runs once. It sets the counter to zero (0), and returns a function expression.
This way add becomes a function and can access the counter in the parent scope.
This makes it possible for the function to have “private” variables.
The secondAdd is to demonstrate that when the outer function id assigned a new variable (and therefore the outer function is also called again) it resets the counter to 0 (for secondAdd) and leaves the add function’s counter at 3.
In this example, we have defined a function addFactory(x) which takes a single argument x and returns a new function. The function it returns takes a single argument y, and returns the sum of x and y. In essence, addFactory is a function factory — it creates functions which can add a specific value to their argument. In the above example we use our addFactory to create two new functions — one that adds 5 to its argument, and one that adds 10. add5 and add10 are both closures. They share the same function body definition, but store different environments. In add5‘s environment, x is 5. As far as add10 is concerned, x is 10.
Closures store references to the outer function’s variables; they do not store the actual value. Closures get more interesting when the value of the outer function’s variable changes before the closure is called.
In the following example, by the time the anonymous functions are called, the value of i is 3 (the length of the array and then it increments). The number 3 was added to the uniqueID to create 103 for ALL the celebritiesID. So every position in the returned array get id = 103, instead of the intended 100, 101, 102.
The reason this happened was because the closure (the anonymous function in this example) has access to the outer function’s variables by reference, not by value. So just as the previous example showed that we can access the updated variable with the closure, this example similarly accessed the i variable when it was changed, since the outer function runs the entire for loop and returns the last value of i, which is 103.
To fix this we can change var to let on line 106. For more information on the recent gift of let visit MDN.
Creating closures within loops can have misleading results. An example of this is shown below. In this example, three buttons are created. When “button1” is clicked, an alert should be displayed that says “Clicked button 1”. Similar messages should be shown for “button2” and “button3”. However, when this code is run, all of the buttons show “Clicked button 4”. This is because, by the time one of the buttons is clicked, the loop has finished executing, and the loop variable has reached its final value of four.
To solve this problem in the past, the closure must have been decoupled from the actual loop variable. This can be done by calling a new function, which in turn creates a new referencing environment. The following example shows how this is done. The loop variable is passed to the getHandler() function. getHandler() then returns a closure that is independent of the original “for” loop.
But thanks to ECMAScript 2015 (6th Edition) we can now change var to let again on line 161 to avoid this bug.
Originally published at journeythroughtheshell.wordpress.com on November 15, 2016.