Understanding Closures
Recently while organizing JavaScript fundamentals, I found the concept of
closure
wasn’t very clear, so I studied it for a while. As they say, “leaving footprints in snow,” I’m documenting this here.
What is a Closure
A function bundled together with references to its surrounding state (lexical environment)
forms a closure. In other words, closures give you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
From MDN
Notes
- Function scope, not block scope
- Closures are stateful functions
Let’s start with some examples to see which is a closure
Example 1
function init() {
var name = 'Mozilla'; // name is a local variable created by init
function displayName() {
// displayName() is the inner function
alert(name); // uses variable declared in parent function
}
displayName();
}
init();
Example 2
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
TIP
Note that Chrome’s debugging state clearly shows which is a closure
. This helps with understanding.
Through these examples, we can actually say:
A closure is a function object that can access variables in another function's scope
, and you can also say a closure is a stateful function
.
Using Closures
Now that we know what closures are, let’s see what they can do.
- Encapsulating private variables
- Modules
- Using in block scope
- Event listeners
We may consciously or unconsciously use these patterns, perhaps without realizing it.
Closures are a Technique, Not Language-Specific
As mentioned above, closure is a technical approach that’s language-independent. So does Java have closures? Yes, it does.
Closures in Java
For example, in lambda expressions, we pass in external function variables, forming closures.
int factor = 3;
numbers.stream()
.filter(e -> e % 2 == 0)
.map(e -> e * factor)
.collect(toList());
Why is this a closure? Note that factor is from the outer function’s lexical environment.
Conceptual Confusion - Lambda, Anonymous Functions, Closures
- Lambda is an anonymous function, just different terminology
- Closures are different from anonymous functions. In the above example, if we remove factor, it’s no longer a closure
Practice Problems
After understanding closures, let’s look at some more complex problems.
Problem 1
function fun(n, o) {
console.log(o);
return {
fun: function(m) { // ③
debugger;
return fun(m, n); // ④
}
};
}
// First example
var a = fun(0); // returns undefined
a.fun(1); // returns 0
a.fun(2); // returns 0
a.fun(3); // returns 0
// Second example
var b = fun(0)
.fun(1)
.fun(2)
.fun(3); // undefined,0,1,2
// Third example
var c = fun(0).fun(1);
c.fun(2);
c.fun(3); // undefined,0,1,1
Problem 2
var myObject = {
foo: 'bar',
func: function () {
var self = this;
console.log('outer func: this.foo = ' + this.foo); // bar
console.log('outer func: self.foo = ' + self.foo); // bar
(function () {
console.log('inner func: this.foo = ' + this.foo); // undefined
console.log('inner func: self.foo = ' + self.foo); // bar
})();
},
};
myObject.func();
- This problem theoretically tests the
this binding
concept. I remember reading a book that had a great line: “this always points to the object that calls it.” Here, that’s myObject. - func and its context
Final Thoughts
The programming world values simplicity and elegance as beauty. Often, if you find a concept very complex, you might be misunderstanding it.
I deeply agree with this statement. Approaching concepts like closures with this mindset should make them simpler.