Closures are functions that can access free variables (variables are used locally, but are defined in closures). In other words, a function defined in a closure can "remember" the environment in which it was created.
Lexical scopes
Consider the following function:
function init () {
var name = "Mozilla";
function DisplayName () {
alert (name);
}
DisplayName ();
}
Init ();
The function init () creates a local variable name, and then defines a function named DisplayName (). DisplayName () is an intrinsic function-defined within init () and only available within the function body. DisplayName () does not have any of its own local variables, however it can access the variables of the external function, that is, the name variable declared in the parent function can be used.
Running the code can find that this works correctly. This is an example of a lexical scope: in JavaScript, the scope of a variable is determined by where it is located in the source code (obviously), and the nested function can access variables declared in its outer scope.
Closed Package
Now consider the following example:
function Makefunc () {
var name = "Mozilla";
function DisplayName () {
alert (name);
}
return displayName;
}
var myFunc = Makefunc ();
MyFunc ();
The effect of running this code is exactly the same as the previous init () example: the string "Mozilla" will be displayed in a JavaScript warning box. The difference-and the interesting part-is that the displayName () inner function is returned from its perimeter function before execution.
This code looks awkward but works fine. In general, local variables in a function are only available during the execution of a function. Once Makefunc () is executed, it is reasonable to assume that the name variable will no longer be available. Although the code works fine, it's not.
The answer to this puzzle is that myFunc becomes a closed bag. Closures are a special kind of object. It consists of two parts: a function, and the environment in which the function is created. An environment consists of any local variables that are in scope when the closure is created. In our example, MyFunc is a closure, formed by the "Mozilla" string that exists when the DisplayName function and the closure are created.
Here's a more interesting example of the-makeadder function:
function Makeadder (x) {
return function (y) {
return x + y;
};
}
var add5 = Makeadder (5);
var add10 = Makeadder (10);
Console.log (ADD5 (2));//7
Console.log (ADD10 (2));//
In this example, we define the Makeadder (x) function: With a parameter x and return a new function. The returned function has a parameter Y, and returns the and of X and Y.
Essentially, Makeadder is a function factory-creating a function that sums the specified value and its arguments, in the example above, we created two new functions using a function factory-one that sums its arguments and 5, the other and 10. Both the
Add5 and add10 are closures. They share the same function definitions, but save different environments. In the ADD5 environment, X is 5. In Add10, X is 10. The
practical closure
theory is all of this-but does the closure really help? Let's look at the practical implications of closures. Closures allow the function to be connected to some of the data (environment) it is manipulating. This is obviously similar to object-oriented programming. In the face of like programming, objects allow us to associate certain data (properties of an object) with one or more methods.
Thus, in general, you can use closures in places where you can use objects with only one method.
in the Web, you might want to do this in a very common situation. Most of the Web JavaScript code we write is event-driven-defining a behavior and then adding it to a user-triggered event (such as tapping or pressing a button). Our code is typically added as a callback: a function that executes in response to an event.
Here's a practical example: Let's say we want to add some buttons to the page to adjust the font size. One method is to specify the font-size of the body element in pixels, and then set the font size of other elements (such as headers) in the page by relative EM units:
Body {
Font-family:helvetica, Arial, Sans-serif;
font-size:12px;
}
H1 {
Font-size:1.5em;
}
H2 {
Font-size:1.2em;
}
Our interactive text size buttons can modify the Font-size property of the body element, and because we use relative units, other elements in the page are adjusted accordingly.
Here's JavaScript:
function Makesizer (size) {
return function () {
document.body.style.fontSize = size + ' px ';
};
}
var size12 = Makesizer (12);
var size14 = Makesizer (14);
var size16 = Makesizer (16);
Size12,size14 and Size16 are functions that adjust the body text accordingly to 12,14,16 pixels. We can add them separately to the button (here is the link). As shown below:
document.getElementById (' size-12 '). onclick = Size12;
document.getElementById (' size-14 '). onclick = Size14;
document.getElementById (' size-16 '). onclick = Size16;
<a href= "#" id= "size-12" >12</a>
<a href= "#" id= "size-14" >14</a>
<a href= "#" id= "size-16" >16</a>
Simulating private methods with closures
Some languages, such as Java, support the declaration of methods as private, that is, they can only be called by other methods in the same class.
For this, JavaScript does not provide native support, but you can use closures to emulate private methods. Private methods are not only useful for restricting access to code: It also provides the power to manage the global namespace, avoiding the non-core approach of confusing the public interface portion of the code.
The following example shows how a closure is used to define a public function, and it can access private functions and variables. This is also known as the module pattern:
var Counter = (function () {
var privatecounter = 0;
Function Changeby (val) {
Privatecounter + = val;
}
return {
Increment:function () {
Changeby (1);
},
Decrement:function () {
Changeby (-1);
},
Value:function () {
return privatecounter;
}
}
})();
Console.log (Counter.value ());/* Logs 0 */
counter.increment ();
Counter.increment ();
Console.log (Counter.value ());/* Logs 2 */
Counter.decrement ();
Console.log (Counter.value ()); /* Logs 1 */
There are a lot of details here. In previous examples, each closure had its own environment, and this time we created only one environment, shared by three functions: Counter.increment,counter.decrement and Counter.value.
The shared environment is created in an anonymous function body, which is executed immediately once defined. The environment contains two private items: A variable named Privatecounter and a function named Changeby. Neither of these items can be accessed directly outside the anonymous function. The three public functions that must be returned by the anonymous wrapper are accessed.
These three public functions are closures that share the same environment. Thanks to the scope of the lexical scope of JavaScript, they all have access to the Privatecounter variable and the Changeby function.
You should notice that we define an anonymous function to create the counter, call the function directly, and assign the return value to the counter variable. You can also save this function to another variable in order to create multiple counters.
var makecounter = function () {
var privatecounter = 0;
function Changeby (val) {
Privatecounter + = val;
Return {
Increment:function () {
Changeby (1);
},
Decrement:function () {
Changeby ( -1);
}, value:function () {
return privatecounter;
}
}
};
var Counter1 = Makecounter ();
var Counter2 = Makecounter ();
Console.log (Counter1.value ());/* Logs 0 */
counter1.increment ();
Counter1.increment ();
Console.log (Counter1.value ());/* Logs 2 */
Counter1.decrement ();
Console.log (Counter1.value ()); /* Logs 1 */
Console.log (Counter2.value ())///* Logs 0 */
Note how two counters maintain their independence. Each time the Makecounter () function is called, its environment is different. In each invocation, the Privatecounter contains different instances.
This form of closure provides many of the benefits that are typically enjoyed by object-oriented programming u, especially data hiding and encapsulation.
Create a closure in a loop: a common error
before the introduction of the Let keyword in JavaScript 1.7, a common problem with closures occurs when a closure is created in a loop. Refer to the following example:
<p id= "Help" >helpful notes would appear here</p>
<p>e-mail: <input type= "text" id= " Email "name=" email "></P>
<p>name: <input type=" text "id=" name "name=" name "></P>
<p>age: <input type= ' text ' id= ' age ' name= ' age ' ></p>
function ShowHelp (help) {
document.getElementById (' help '). InnerHTML = Help;
}
function Setuphelp () {
var helpText = [
{' id ': ' email ', ' help ': ' Your e-mail address '},
{' id ': ' name ', ' Help ': ' Your full Name '},
{' id ': ' Age ', ' help ': ' Your age ' (You must is over 16) '}
];
for (var i = 0; i < helptext.length; i++) {
var item = Helptext[i];
document.getElementById (item.id). onfocus = function () {
ShowHelp (ITEM.HELP);
}
}
}
Setuphelp ();
The array helpText defines three useful hints, each associated with the ID of the input field in the corresponding document. By looping through the three definitions, a onfocus event handler is added to each input field to display the help information.
After you run this code, you'll see that it doesn't get the effect you want. Regardless of which input field the focus is on, information about age is displayed.
The problem is that the assignment to onfocus is an anonymous function in the closure (SETUPHELP) instead of a closure object; Altogether three anonymous functions are created in the closure (SETUPHELP), but they all share the same environment (item). When the onfocus callback is executed, the loop has already completed, and the item variable (shared by all three closures) has pointed to the last item in the HelpText list.
One solution to this problem is to make onfocus point to a new closure object.
function ShowHelp (Help) {
document.getElementById (' help '). InnerHTML = Help;
}
function Makehelpcallback (Help) {
return function () {
ShowHelp (Help);
};
}
function Setuphelp () {
var helpText = [
{' id ': ' email ', ' help ': ' Your e-mail address '},
{' id ': ' name ', ' Help ': ' Your full Name '},
{' id ': ' Age ', ' help ': ' Your age ' (You must is over 16) '}
];
for (var i = 0; i < helptext.length; i++) {
var item = Helptext[i];
document.getElementById (item.id). onfocus = Makehelpcallback (ITEM.HELP);
}
}
Setuphelp ();
This code can work as we expect. All callbacks no longer share the same environment, and the Makehelpcallback function creates a new environment for each callback. In these environments, help points to the corresponding string in the HelpText array.
Performance Considerations
If a closure is not required for some particular task, it is unwise to create a function in another function if it is not necessary, because closures have a negative impact on script performance, including processing speed and memory consumption.
For example, when you create a new object or class, the method should usually be associated with the object's prototype, rather than being defined in the object's constructor. The reason is that this will cause each constructor to be called, and the method will be re-assigned once (that is, for each object creation).
Consider the following example, which is impractical but indicates a problem:
function MyObject (name, message) {
THIS.name = name.tostring ();
This.message = Message.tostring ();
This.getname = function () {
return this.name;
};
This.getmessage = function () {
return this.message;
};
}
The above code does not take advantage of the benefits of closures, so it should be modified to the following general form:
function MyObject (name, message) {
THIS.name = Name.tostring ();
This.message = Message.tostring ();
}
Myobject.prototype = {
Getname:function () {
return this.name;
},
Getmessage:function () {
return this.message;
}
};
or change to:
function MyObject (name, message) {
THIS.name = Name.tostring ();
This.message = Message.tostring ();
}
MyObject.prototype.getName = function () {
return this.name;
};
MyObject.prototype.getMessage = function () {
return this.message;
};
In the previous two examples, the inherited prototypes can be shared for all objects and do not have to define the method every time the object is created.
Closure Pack 02