closure
2021/5/3
What is a closure?
- A combination of a function bound to (or surrounded by) references to its surrounding state (lexical environment) is a closure.
- Closures allow you to access the scope of an outer function in an inner function.
- In JavaScript, whenever a function is created, the closure is created at the same time the function is created.
Lexical scope
-
function init() { var name = "Mozilla"; // name is a local variable created by the init function function function displayName() {// displayName() is an inner function, a closure alert(name); // use a variable declared in the parent function} displayName(); } init();Copy the code
displayName()
Is defined in theinit()
In, and only ininit()
Function body is available.- Because it can access variables of external functions, so
displayName()
Parent functions can be usedinit()
A variable declared inname
。
-
This example of lexical scope describes how the parser resolves variable names in nested functions.
-
The word lexical refers to lexical scope determining where a variable is available based on where it is declared in the source code.
-
Nested functions can access variables declared outside their scope.
3. Closure
-
function makeFunc() { var name = "Mozilla"; function displayName() { alert(name); } return displayName; } var myFunc = makeFunc(); myFunc(); Copy the code
- Internal function
displayName()
Before implementation, returns from an external function. But running this code has the same effect as beforeinit()
The example of the function is exactly the same. - The reason is that functions in JavaScript form closures.
- closureIs a combination of functions and the lexical environment in which the function is declared. The environment contains any local variables that were in scope when the closure was created.
- In this case,
myFunc
Is to performmakeFunc
Created whendisplayName
A reference to a function instance. displayName
The instance maintains a lexical environment (variable) for itname
Exists within).- Therefore, when
myFunc
When called, the variablename
Still available.
- Internal 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)); / / 12Copy the code
makeAdder
It’s a function factoryadd5
和add10
They’re all closures.- They share the same function definition, but preserve different lexical environments.
4. Closure practice
1. Change the page size
-
function makeSizer(size) { return function() { document.body.style.fontSize = size + 'px'; }; MakeSizer (12); makeSizer(12); makeSizer(12); makeSizer(12); var size14 = makeSizer(14); var size16 = makeSizer(16);Copy the code
2. Simulate private methods
-
Private methods are not just good for limiting access to code: they also provide a powerful ability to manage global namespaces, preventing non-core methods from messing up the public interface portion of code.
-
JavaScript does not have such native support for an API for declaring methods private
-
But we can use closures to simulate private methods.
-
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 */Copy the code
- This example creates only one lexical environment, shared by the three functions.
- The shared environment contains two private items, neither of which can be accessed directly outside the anonymous function. It must be accessed through three public functions returned by anonymous functions.
- These three public functions are closures that share the same environment, and JavaScript’s lexical scope makes them all accessible
privateCounter
Variables andchangeBy
Function.
change
-
Change the anonymous immediate function to a normal anonymous function and assign to a variable
-
var makeCounter = function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; }}}; // Each closure refers to a variable in its own lexical scope. // Changes to variables in one closure change the lexical context of that closure, but do not affect variables in the other closure. 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 */Copy the code
3. Solve the problem of var variable promotion in the for loop
Explain the for loop
- The for loop creates a block each time it loops, and the for loop ends in a very short time
- Variables created by var do not have block-level scope and are promoted
- So the value of the variable declared by var in the last block becomes the latest value because the variable is promoted
The sample
-
<p id="help">Helpful notes will appear here</p> <input type="text" id="email" name="email"> <input type="text" id="name" name="name"> <input type="text" id="age" name="age"> ------------------------------------------------------------------------------------- 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 be over 16)'} ]; for (var i = 0; i < helpText.length; i++) { var item = helpText[i]; document.getElementById(item.id).onfocus = function() { showHelp(item.help); } } } setupHelp();Copy the code
- Assigned to
onfocus
Is the closure. These closures are defined by their functions and insetupHelp
Of the environment captured in a scope. - These three closures are created in the loop, but they share the same lexical scope, where there is a variable item.
- This is because the variable item is declared using var and has function scope because the variable is promoted.
- when
onfocus
The callback is executed when the variable objectitem
(shared by three closures) already pointed tohelpText
The last term.
- Assigned to
The solution
1. Use more closures
-
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 be over 16)'} ]; for (var i = 0; i < helpText.length; i++) { var item = helpText[i]; document.getElementById(item.id).onfocus = makeHelpCallback(item.help); } } setupHelp(); Copy the code
-
Create a new lexical environment for each callback using the factory function makeHelpCallback.
-
All callbacks no longer share the same environment, where help points to the corresponding string in the helpText array.
2. Use anonymous closures
-
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 be over 16)'} ]; for (var i = 0; i < helpText.length; i++) { (function() { var item = helpText[i]; document.getElementById(item.id).onfocus = function() { showHelp(item.help); }}) (); }} setupHelp();Copy the code
3. Use let keyword
-
for (var i = 0; i < helpText.length; i++) { let item = helpText[i]; // Use let instead of var, so each closure is bound to block-scoped variables, This means that the additional closure document.getelementById (item.id).onfocus = function() {showHelp(item.help); }}Copy the code
Use 4.forEach()
To traversehelpText
An array of
-
helpText.forEach(function(text) { document.getElementById(text.id).onfocus = function() { showHelp(text.help); }});Copy the code
5. Performance considerations
-
It is unwise to create functions in other functions that do not require closures for some specific task, because closures have a negative impact on script performance in terms of processing speed and memory consumption.
-
When creating a new object or class, methods should usually be associated with the object’s prototype rather than defined in the object’s constructor.
-
The reason is that this causes the method to be reassigned each time the constructor is called (that is, for each object created, the method is reassigned).
-
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; }; Copy the code