So it took me awhile to truly understand JavaScript closures. There is limited documentation of the subject on the web, but here is a list of the resources I used to finally grasp not only what specifically creates a closure, but also why we would want to use them.
- JavaScript: the Definitive Guide
- Douglas Crockford: Private Members in JavaScript
- JavaScript Closures for Dummies
- jibbering.com/faq/faq_notes/closures.html
First, let’s define closures:
- A “closure” is an expression (typically a function) that can have free variables together with an environment that binds those variables (that “closes” the expression). - Jim Ley
- …JavaScript has closures. What this means is that an inner function always has access to the vars and parameters of its outer function, even after the outer function has returned. This is an extremely powerful property of the language. - Douglas Crockford
- Things are different if you save a reference to the nested function in the global scope. You do so by using the nested function as the return value of the outer function or by storing the nested function as a property of some other object. - JavaScript: the Definitive Guide
- Simply put, a closure is a variable, created inside a function, which continues to exist after the function has finished executing. - Patrick Hunlock
I should preface the more detailed explanation below with a few prerequisite concepts. If you don’t truly grasp these concepts, then you may get hung up when trying to understand JavaScript closures. Fortunately, your quest to understand closures should force you to understand these concepts on a deeper level first.
- global object and global scope
- reference types and primitive types
- lexical scope and the scope chain
- the call object or the ECMA activation object
- idea of persistent data
- private variables
- using return
To begin, we want to create a reusable function containing data that persists across invocations.
We don’t want to hard code variables into the reusable function, and a local variable will not persist.
From what we have learned by our ‘Responsible JavaScript for the Enterprise’ guidelines and through our understanding of the call object, lexical scope, and namespacing: we also know that we want to greatly limit global variables in our global scope.
So instead, we want the developer to make two calls: One, to set up or ‘configure’ the function, and two, to invoke this ‘pre-configured’ function.
The power is in the first call. I like to refer to it as the configuration call.
During the configuration call, we ‘freeze’ the inner function by setting a reference to it in the global scope.
So this data persists. Why?
We have an external reference to this inner function.
The inner nested function retains its reference to the call object of the outer function.
The outer function’s local scope resolves and the reference to its inner function remains.
This is an example from a friend that helped me finally wrap my head around the power of the technique.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|