- What is a closure?
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 argumentsobject, however, even though it can call the outer function’s parameters directly.
You create a closure by adding a function inside another function.
A Basic Example of Closures in JavaScript:
| function showName (firstName, lastName) { | |
| var nameIntro = "Your name is "; | |
| // this inner function has access to the outer function's variables, including the parameter | |
| function makeFullName () { | |
| return nameIntro + firstName + " " + lastName; | |
| } | |
| | |
| return makeFullName (); | |
| } | |
| | |
| showName ("Michael", "Jackson"); // Your name is Michael Jackson |
Closures are used extensively in Node.js; they are workhorses in Node.js’ asynchronous, non-blocking architecture. Closures are also frequently used in jQuery and just about every piece of JavaScript code you read.
A Classic jQuery Example of Closures:
A Classic jQuery Example of Closures:
| $(function() { | |
| | |
| var selections = []; | |
| $(".niners").click(function() { // this closure has access to the selections variable | |
| selections.push (this.prop("name")); // update the selections variable in the outer function's scope | |
| }); | |
| | |
| }); |
Closures’ Rules and Side Effects
- Closures have access to the outer function’s variable even after the outer function returns:
One of the most important and ticklish features with closures is that the inner function still has access to the outer function’s variables even after the outer function has returned. Yep, you read that correctly. When functions in JavaScript execute, they use the same scope chain that was in effect when they were created. This means that even after the outer function has returned, the inner function still has access to the outer function’s variables. Therefore, you can call the inner function later in your program. This example demonstrates:function celebrityName (firstName) { var nameIntro = "This celebrity is "; // this inner function has access to the outer function's variables, including the parameter function lastName (theLastName) { return nameIntro + firstName + " " + theLastName; } return lastName; } var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned. // The closure (lastName) is called here after the outer function has returned above // Yet, the closure still has access to the outer function's variables and parameter mjName ("Jackson"); // This celebrity is Michael Jackson - 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. And this powerful feature can be harnessed in creative ways, such as this private variables example first demonstrated by Douglas Crockford:
function celebrityID () { var celebrityID = 999; // We are returning an object with some inner functions // All the inner functions have access to the outer function's variables return { getID: function () { // This inner function will return the UPDATED celebrityID variable // It will return the current value of celebrityID, even after the changeTheID function changes it return celebrityID; }, setID: function (theNewID) { // This inner function will change the outer function's variable anytime celebrityID = theNewID; } } } var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned. mjID.getID(); // 999 mjID.setID(567); // Changes the outer function's variable mjID.getID(); // 567: It returns the updated celebrityId variable - Closures Gone Awry
Because closures have access to the updated values of the outer function’s variables, they can also lead to bugs when the outer function’s variable changes with a for loop. Thus:// This example is explained in detail below (just after this code box). function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function () { return uniqueID + i; } } return theCelebrities; } var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}]; var createIdForActionCelebs = celebrityIDCreator (actionCelebs); var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id()); // 103 In the preceding 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, as we have discussed in the previous example, 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 side effect (bug) in closures, you can use an Immediately Invoked Function Expression (IIFE), such as the following:function celebrityIDCreator (theCelebrities) { var i; var uniqueID = 100; for (i = 0; i < theCelebrities.length; i++) { theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE return function () { return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array } () // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function. } (i); // immediately invoke the function passing the i variable as a parameter } return theCelebrities; } var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}]; var createIdForActionCelebs = celebrityIDCreator (actionCelebs); var stalloneID = createIdForActionCelebs [0]; console.log(stalloneID.id); // 100 var cruiseID = createIdForActionCelebs [1]; console.log(cruiseID.id); // 101
Our Career Paths and Courses Website Is Now Live
Learn.Modern Developer Launched
Our first cohort is in session: 97% of our first cohort on target to graduate. Enroll in the second cohort. Career Path 1: JavaScript Developer and Career Path 3: Modern Frontend Developer usually fill up quickly.
- why closure
- Using closure to hide state
My favorite use of closure is to call a function that generates another function or group of functions but hides all the state in private variables within the closure:
function greeter(name, age) {
var message = name + ", who is " + age + " years old, says hi!";
return function greet() {
console.log(message);
};
}
// Generate the closure
var bobGreeter = greeter("Bob", 47);
// Use the closure
bobGreeter();
var message = name + ", who is " + age + " years old, says hi!";
return function greet() {
console.log(message);
};
}
// Generate the closure
var bobGreeter = greeter("Bob", 47);
// Use the closure
bobGreeter();
for security
I've used closures to do things like:
As you can see there,
a is now an object, with a method publicfunction ( a.publicfunction() ) which calls privatefunction, which only exists inside the closure. You can NOT call privatefunction directly (i.e. a.privatefunction() ), just publicfunction().
Its a minimal example but maybe you can see uses to it? We used this to enforce public/private methods.
Using a closure instead of objects
Many people who come to JavaScript are experienced programmers who come from other languages where classes and instances are the common way to handle this encapsulation. JavaScript has something similar in the form of constructor functions and function prototypes.
Classical OO in JavaScript
Consider the following class, it uses a classical constructor with function prototypes to work like a class from other languages.
Since you're using the object itself as the place to store state, all references have to be prefixed with
this. It's impossible to hide any variables since everything that accessible to your methods is also publicly readable, writable, and even deletable. Also if you have a function nested inside of anything then this will change on you unless it's explicitly passed through or preserved with a closure. (see the slowGreet method)
Define the class like this:
personclass.js
And use it like this:
useclass.js
Nice clean OO code right? The good thing is that you get to write your methods outside of the constructor instead of nested inside it. This is a very comfortable pattern and is used by a lot of successful JavaScript projects.
Object factories using closures
This is how I would write this class without using
new and prototype. I'll create a factory function that creates a closure and exposes parts of it as public methods. Externally it looks a lot like the class based version, but internally it's 100% a closure and there isn't a this or new in sight.
Define the factory like this:
personfactory.js
And use it like this:
usefactory.js
I like it! One word of caution though. While this method is quite easy to use, it doesn't perform well when you're creating large numbers of instances. Each instance will create its own version of every function in the object.
Closures for events and callbacks
This is where closures are the most useful. In fact, this is the reason that Ryan Dahl (The creator of node.js) used JavaScript in the first place. C doesn't have closures and it makes non-blocking code difficult to write in C.
The simplest example (which we just saw earlier) is
setTimeout. This is a non-blocking function call. The code in the passed in callback won't get called till after the timeout happens. This will be on a completely new stack and the only way to get data into it is through lexical scope and a closure.
Imagine this code snippet:
settimeout.js
This won't work,
message will be undefined since it's a local variable to setAlarm and doesn't exist outside that function. Instead we need to define the handle function inside of the setAlarm function.
settimeout2.js
As explained in the "What is This?" article,
this is especially painful when dealing with setting callbacks. This is because specifying a method of an object as the callback function will cause the function by itself to be the callback, not the object associated with it.
eventobj.js
Interesting thing about JavaScript is that functions are first-class values. The whole
this context helps in designing classical OO API's, but you have to remember that it's only assigned on function call, it's not something tied to the function itself. Variables from the closure, however are part of the function itself, no matter how it's called. |
No comments:
Post a Comment