What is closure?
Closure is a function that has access to free variables (variables are used locally, but defined in closures). In other words, functions defined in closures can "remember" the environment in which they were created.
Function scope and declaration advance
var scope= "global scope";
function f(){
console.log(scope); //undefined, there is no "global scope"
var scope="local scope";
console.log(scope);//local scope
}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
Why?
JavaScript The scope of a function is that all variables declared in a function are always visible in the body of the function. This feature is also called "pre-declaration".
Namely javascript All declared variables in a function (but not assignments) are advanced to the top of the function body.
The above code is similar to
var scope= "global scope";
function f(){
var scope;
console.log(scope);
scope="local scope";
console.log(scope);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Scope chain
First, each piece of code (global code or function) has a scope chain associated with it, which can be a list of objects or a linked list, which defines the variables in the scope of the code.
When javscript needs to find the value of variable x (variable resolution), it starts with the first object in the chain. If the object has an attribute named x, it uses the value of x; otherwise, it looks for the next object in the chain; and so on, until the top, if it is still not found, it throws a Reference Error. Chang.
At the top level of Javascript, the scope chain consists of a global object.
In a function body that does not contain nested functions, there are two objects in the scope chain: the first is the defined function parameters (lenth, caller, arguments... ) Objects with local variables; the second is global objects.
In a nested function body, there are at least three objects in the scope chain.
For nested functions, every time an external function is called, the internal function is redefined.
Because although. The code of the internal function is different, but the scope chain has changed.
What is closure?
The definition of closure in various professional literature is very abstract and difficult to understand. My understanding is that closures are functions that can read internal variables of other functions.
Because in Javascript, only subfunctions within functions can read local variables, closures can be simply understood as "functions defined within a function".
So, in essence, closure is a bridge connecting the function interior and exterior.
Purpose 1: External functions can read variables of internal functions
function f1(){
var n=999;
function f2(){
alert(n);
}
return f2;
}
var result=f1();
result(); // 999
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Here the external function can read n in f1(), because the internal function f2() can read the local variables of f1, and by returning f2, the scope chain of F2 has n, so the value of N can be obtained.
My understanding:
Purpose 2: Keep variables of internal functions in memory
var uniqueInteger = (function() {
var counter = 0;
return function() {
return counter++;
}
}());
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
Practical closures
Generally speaking, closures can be used where objects with only one method can be used.
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 */
- 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
- 26
- 27
- 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
- 26
- 27
In a Counter, increment, decrement, and value share scoping environments, i.e., they can access the same privateCounter and changeBy(), so that a simple technicians can be implemented.
This is also an advantage of closure memory sharing.
Counter1 and Counter2 have different creation environments and scope chains. privateCounter and changeBy() are accessed at different memory addresses.
Each time the makeCounter() function is called, its environment is different. In each call, privateCounter contains different instances.
Creating closures in loops: a common error
<p id="help">Helpful notes will 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>
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
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(); //// Stay at'Your age'every time.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
//The effect we want
document.getElementById(helpText[0].id).onfocus = function() {
showHelp(helpText[0].help);
};
document.getElementById(helpText[1].id).onfocus = function() {
showHelp(helpText[1].help);
};
document.getElementById(helpText[2].id).onfocus = function() {
showHelp(helpText[2].help);
};
//Actual effect
document.getElementById(helpText[0].id).onfocus = function() {
showHelp(helpText[2].help);
};
document.getElementById(helpText[1].id).onfocus = function() {
showHelp(helpText[2].help);
};
document.getElementById(helpText[2].id).onfocus = function() {
showHelp(helpText[2].help);
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
The reason for this problem is that onfocus is assigned to an anonymous function in a closure (setupHelp) rather than a closure object; three anonymous functions are created in a closure (hereinafter referred to as getElenmentById (.). onfocus () = fucntion (){ }
But they all share the same environment. When the onfocus callback is executed, the loop is already complete, and the item variable (shared by all three closures) has pointed to the last item in the helpText list.
Understanding: The setupHelp() function is executed to create a functional environment, and the call to getElenmentById(..).onfocus() essentially modifies the item in the same environment.
showHelp (helpText[2].help) is executed when the environment is initialized.
The three loops end first. In fact, when onfocus() is executed, all the incoming showHelp is to help Text [2]. help.
One solution to this problem is to point onfocus 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 be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
}
}
setupHelp();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
Finally, use closures with caution
Because it involves occupying memory, because every time a function object is generated, it opens up a certain space in memory to store its environment, so closures are used appropriately.
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
this.getName = function() {
return this.name;
};
this.getMessage = function() {
return this.message;
};
}
var newobj1=MyObject('x','hello');
var newobj2=MyObject('y','javascript');
newobj1.getName()==newobj2.getName();//false
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
The downside of this is that each time MyObject is constructed, new memory is partitioned for getName() and getMessage(), which is not cost-effective.
Just write him in the form below.
Because the method in the prototype points to the same piece of memory (suddenly wondering if prototype can be understood as a pointer, it points to areas where prototype functions and user-defined prototype functions are placed)
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype = {
getName: function() {
return this.name;
},
getMessage: function() {
return this.message;
}
};
var newobj1=MyObject('x','hello');
var newobj2=MyObject('y','javascript');
newobj1.getName()==newobj2.getName();//true
This article has been referred to.< javascript Authoritative Guide
as well ashttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
as well ashttp://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
Make sure closures are clear
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15