1) An Example of closure and scope (extract from JavaScript Design Patterns):
In this example, a is defined in the function 'outerFunc', but the function 'innerFunc' can access it because
'innerFunc' is also defined within 'outerFunc'.
function outerFunc() {
var a = 10;
function innerFunc() {
a *=2;
return a;
}
return innerFunc;
}
var newF = outerFunc(); //newF is now a reference to function 'innerFunc'.
newF(); //returns 20.
newF(); //returns 40.
newF(); //returns 80.
var difF = outerFunc(); // difF is another reference to 'innerFunc'.
difF(); //returns 20, because a new copy of a is being used.
Here 'a' reference to the function 'innerFunc' is returned and assigned to the variable 'newF'. This
function is now executed outside of 'outerFunc', and it still has access to 'a'.
This is possible because JavaScript is lexically scoped.
Functions run in the scope they are defined in (in this case, the
scope within 'outerFunc'), rather than the scope they are executed in. As long as 'innerFunc' is defined within
'outerFunc', it has access to all of outerFunc’s variables, even if 'outerFunc' is finished executing.
This is an example of a CLOSURE.
The most common way of creating a closure is by returning a nested function.
2) Closure (extract from JavaScript Good Parts)
We will initialize myObject by calling a function that returns an object literal. That function defines a value variable. That variable is always available to the increment and getValue methods, but the function's scope keeps it hidden from the rest of the program:
var myObject = function ( ) {
var value = 0;
return {
increment: function (inc) {
value += typeof inc === 'number' ? inc : 1;
},
getValue: function ( ) {
return value;
}
}
}( );
We are not assigning a function to myObject. We are assigning the result of invoking that function. Notice the ( ) on the last line. The function returns an object containing two methods, and those methods continue to enjoy the privilege of access to the value variable.
3) Closure (extract from JavaScript Good Parts)
It is important to understand that the inner function has access to the actual variables of the outer functions and not copies in order to avoid the following problem:
Code View:
// BAD EXAMPLE
// Make a function that assigns event handler functions to an array
of nodes the wrong way.
// When you click on a node, an alert box is supposed to display the ordinal
of the node.
// But it always displays the number of nodes instead.
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i);
}
}
};
// END BAD EXAMPLE
The add_the_handlers function was intended to give each handler a unique number (i). It fails because the handler functions are bound to the variable i, not the value of the variable i at the time the function was made:
Code View:
// BETTER EXAMPLE
// Make a function that assigns event handler functions to an array of nodes the right way. // When you click on a node, an alert box will display the ordinal of the node.
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (i) {
return function (e) {
alert(i);
};
}(i);
}
};
Now, instead of assigning a function to onclick, we define a function and immediately invoke it, passing in i. That function will return an event handler function that is bound to the value of i that was passed in, not to the i defined in add_the_handlers. That returned function is assigned to onclick.
Note: I saw this example in real life project, so it's not something, like it only happens in theory.
4) Closure (extract from Object Oriented JavaScript)
When you pass an argument to a function it becomes available as a local variable. You can create a function that returns another function, which in turn returns its parent's argument.function f(arg) {
var n = function(){
return arg;
};
arg++;
return n;
}
You use the function like this:
var m = f(123); m();//result: 124
Notice how arg++ was incremented after the function was defined and yet, when called, m() returned the updated value. This demonstrates how the function binds to its scope, not to the current variables and their values found in the scope.
5) Closures in a Loop (extract from Object Oriented JavaScript)
Here's something that can easily lead to hard-to-spot bugs, because, on the surface, everything looks normal. Let's loop three times, each time creating a new function that returns the loop sequence number. The new functions will be added to an array and we'll return the array at the end. Here's the function:ffunction f() {
var a = [];
var i;
for(i = 0; i < 3; i++) {
a[i] = function() {
return i;
}
}
return a;
}
Let's run the function, assigning the result to the array a.
var a = f(); a[0]() //returns: 3 a[1]() //returns: 3 a[2]() //returns: 3Now you have an array of three functions. Let's invoke them by adding parentheses after each array element. The expected behavior is to see the loop sequence printed out: 0, 1, and 2. Let's try: >>> a[0]() 3 >>> a[1]() 3 >>> a[2]() 3 Hmm, not quite what we expected. What happened here? We created three closures that all point to the same local variable i. Closures don't remember the value, they only link (reference) the i variable and will return its current value. After the loop, i's value is 3. So all the three functions point to the same value. (Why 3 and not 2 is another good question to think about, for better understanding the for loop.)
So how do you implement the correct behavior? You need three different variables.
An elegant solution is to use another closure:
function f() {
var a = [];
var i;
for(i=0; i< 3; i++) {
a[i] = (function(x) {
return function() {
return x;
}
})(i);
}
return a;
}
Usage/Test Result:
var ba = f(); ba[0](); ba[1](); ba[2]();
Here, instead of just creating a function that returns i, you pass i to another self-executing function. For this function, i becomes the local value x, and x has a different value every time.
Alternatively, you can use a "normal" (as opposed to self-invoking) inner function to achieve the same result. The key is to use the middle function to "localize" the value of i at every iteration.
function f() {
function makeClosure(x) {
return function() {
return x;
}
}
var a = [];
var i;
for(i = 0; i < 3; i++) {
a[i] = makeClosure(i);
}
return a;
}
var bc = f(); bc[0](); bc[1](); bc[2]();
6) Closure - Getter/Setter (extract from Object Oriented JavaScript)
Imagine you have a variable that will contain a very specific range of values. You don't want to expose this variable because you don't want just any part of the code to be able to alter its value.
You protect this variable inside a function and provide two additional functions—one to get the value and one to set it. The one that sets it could contain some logic to validate a value before assigning it to the protected variable (let's skip the validation part for the sake of keeping the example simple).
You place both the getter and the setter functions inside the same function that contains the secret variable, so that they share the same scope:
var getValue, setValue;
(function() {
var secret = 0;
getValue = function() {
return secret;
};
setValue = function(v) {
//should perform some validation
secret = v;
}
})();
In this case, the function that contains everything is a self-invoking anonymous function. It defines setValue() and getValue() as global functions, while the secret variable remains local and inaccessible directly.
//getValue(); setValue(123); getValue()
7) Closure - Iterator (extract from Object Oriented JavaScript)
You already know how to loop through a simple array, but there might be cases where you have a more complicated data structure with different rules as to what the sequence of values is.
You can wrap the complicated "who's next" logic into an easy-to-use next() function. Then you simply call next() every time you need the consecutive value. For this example, we'll just use a simple array, and not a complex data structure.
Here's an initialization function that takes an input array and also defines a private pointer i that will always point to the next element in the array.
function setup(x) {
var i = 0;
return function() {
return x[i++];
};
}
Calling the setup() function with a data array will create the next() function for you.
var next = setup (['a', 'b', 'c']);
From there it's easy and fun: calling the same function over and over again gives you the next element.
next(); //returns "a" next(); //returns "b" next(); //returns "c"
To Sum Up:
JavaScript offers a lot of fun with its flexibility.
No comments:
Post a Comment