JavaScript and Scope VII - More Closures
June 10, 2019 • 2 min read
In the previous article on JavaScript and Scope VI - Closures you saw your first example of how lexical scope can result in closures. A closure meaning that inner functions have access to the variables of outer functions. Even when the outer function finishes running.
Another great way of seeing this is with a callback function, since callbacks are still fairly common, especially in older JavaScript.
function runsAnyCallback(callback) {
var i = 10; callback();}
function foo() {
var i = 2;
function bar() {
i = i + 1;
console.log ('value of i when bar is declared inside foo: ', i);
}
return bar;
}
// Get the bar function.
var barFunc = foo();
// foo has already completed running.
runsAnyCallback(barFunc); // logs 3!Think of closures as just another tool to get the results you want. One of the most popular JavaScript questions about scope on StackOverflow is about unexpected values in a for loop.
var funcs = [];
for (var i = 0; i < 4; i++) { // Declare and save a function.
funcs[i] = function() { console.log('My value: ', i);
};
}
// Global variable `i` is already 3.
// Run each function.
funcs[1](); // logs 3
funcs[2](); // logs 3
funcs[3](); // logs 3The above code should make sense to you already because you know from JavaScript and Scope IV - Blocks that blocks do not create new scope when using var. So the i variable exists at the global scope. The final value of the i variable will be 3, before any of the functions run. Each of the functions will just log the current value of the global i variable when they run.
One way to fix this is using a closure.
var funcs = [];
// Outer function creates new scope.
function makeLog (i) {
var j = i; // Declare inner function using new scope.
return function () { console.log('My value: ', j);
}
}
for (var i = 0; i < 4; i++) {
// Run makeLog, but the inner function doesn't run yet.
funcs[i] = makeLog(i);
}
// Run each inner function.
funcs[1](); // logs 1
funcs[2](); // logs 2
funcs[3](); // logs 3In the code above, we take advantage of the fact that functions create a new scope. Every time the outer makeLog function runs, it creates a new and separate j variable in its scope. It then declares and returns an inner function that logs that local j variable. Even when the makeLog function finishes running, that returned function will still reference the unique j variable created each time makeLog runs. Because makeLog ran three times, there are 3 separate j variables.
Note that for learning purposes the above code is intentionally more verbose than needed, and will be improved upon in JavaScript and Scope VIII - Improved Closures.