[TOC]
Posture guide for using JavaScript closures
The introduction
A closure is a function that can access variables in the scope of another function. A closure is a function that can access variables in the scope of other functions. Js has a global object, which is window in a browser, global in a node, and all functions are under this object and can access variables in that object All functions in
Definition of closures
A function and a reference to its state, the lexical environment, form a closure. That is, closures allow you to access the outer function scope from the inner function. In JavaScript, functions generate closures each time they are created. [1]
The MDN definition of closures talks about lexical environments and references as well as generating closure reference code each time it is created
const eg = (a)= >{
let a ='Test variable' // Local variables created by eg
let inner = (a)= >{ // An internal function of eg, a closure
console.log(a) // Use a variable declared in the parent function
}
return inner Inner is the scope where a closure can access the EG function
}
Copy the code
Let’s do an interesting example
function init() {
var name = "Mozilla"; // Name is a local variable created by init
function displayName() { // displayName() is an internal function, a closure
alert(name); // Use a variable declared in the parent function
}
displayName();
}
init();
Copy the code
DispplayName can be accessed to init variable name in parent scope due to js scope
So let’s look at this example
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
Copy the code
This code executes exactly as before, except that the difference – and the interesting part – is that the inner function, displayName(), is returned by the outer function before it executes. You probably think it won’t work, so let’s change the code again
var name2 = 123
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name2);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
Copy the code
You know almost without thinking about it that it’s going to be 123 so why can’t you be sure that it’s going to execute before we return the code
The answer is that functions in JavaScript form closures. Closures are a combination of a function and the lexical context in which it was created. < img SRC = “undefind” style = “box-size: border-box; border-box: border-box; border-box: border-box; border-box: border-box; border-box: border-box; border-box: border-box; border-box: border-box; border-box: border-box
So let’s switch the examples again
var object = { name: GetName: function() {return function() {console.info(this.name)}} object.getName() // undername ()Copy the code
Where does this point at this time? The answer is global because the closure function is executed in the window scope, that is, this points to Windows
Now let’s do another example
function outer() {
var a = Variables' 1 '
var inner = function () {
console.info(a)
}
return inner Inner is a closure function because it has access to the scope of outer
}
var inner = outer() // Get the inner closure function
inner() Variable 1 "/ /"
Copy the code
Var inner = outer(); var inner = outer(); var inner = outer(); var inner = outer(); The book Advanced Programming in JavaScript suggests that because closures carry the scope of the function that contains them, and because they take up more content than other functions, overusing closures can lead to excessive memory usage. [2]
Let’s do another interesting example
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)); / / 12
Copy the code
Add5 and add10 are both closures and share the definition of the function, but hold different lexical environments, x=5 in add5 and x= 10 in ADD10
Memory leak problem
A closure function refers to an outer variable. When the outer function is executed, the variable cannot be released
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
aler(el.id) // This causes the closure to refer to the outer EL, which cannot be released after the showId is executed}}Function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
aler(id) // This causes the closure to refer to the outer EL, which cannot be released after the showId is executed
}
el = null // Release el actively
}
Copy the code
function factorial(num) {
if(num<= 1) {
return 1;
} else {
return num * factorial(num- 1)}}var anotherFactorial = factorial
factorial = nullanotherFactorial(4) Return num* arguments.callee (num-1), arguments.callee refers to the currently executing function, but this property cannot be used in strict mode, so the closure is used to do this
Function newFactorial = (function f(num){
if(num<1) {return 1}
else {
return num* f(num- 1)}})// Then there is no problem. It is the closure f that actually works, not the outer function newFactorial
Copy the code
Use closures to solve the recursive call problem
Simulate private methods with closures
Programming languages, such as Java, support declaring methods private, meaning they can only be called by other methods in the same class.
JavaScript doesn’t have this native support, but we can use closures to 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 parts of the code.
The following example shows how to use closures to define public functions and give them access to private functions and variables. This is also known as a Module pattern
var Counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(- 1);
},
value: function() {
returnprivateCounter; }}}) ();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
In the previous example, each closure had its own lexical environment; This time we created just one lexical environment, shared by three functions: Counter. Increment, Counter. Decrement, and Counter.
The shared environment is created in the body of an anonymous function that executes immediately. This 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. Must be accessed through the three public functions returned by the anonymous function.
These three common functions are closures that share the same environment. Thanks to JavaScript’s lexical scope, they all have access to the privateCounter variable and the changeBy function.
You should notice that we have defined an anonymous function to create a counter. We immediately execute the anonymous function and assign its value to the variable Counter. We can store this function in another variable, makeCounter, and use it 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() {
returnprivateCounter; }}};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
Notice how the two counters, Counter1 and Counter2, maintain their independence. Each closure is a reference to the variable privateCounter in its lexical scope.
Changing the value of this variable each time one of the counters is invoked changes the lexical context of the closure. However, changes to variables in one closure do not affect variables in the other closure.
Using closures in this way offers many of the benefits associated with object-oriented programming — particularly data hiding and encapsulation.
Use closures in the loop
<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>
Copy the code
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
As you can probably imagine from this point on, the end result is that all item.help points to the last item of helpText because they share the same lexical scope. The solution is to use the let keyword or use anonymous closures
// 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);
}
})(); // Immediately associate the item of the current loop item with the event callback
}
}
setupHelp();
// Use the let keyword
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++) {
let item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
Copy the code
Performance considerations
It is unwise to create a function within another function if it is not required for some specific task, because closures have a negative impact on script performance in terms of processing speed and memory consumption.
For example, when creating a new object or class, methods should normally be associated with the object’s prototype, not defined in the object’s constructor. The reason is that this will cause the method to be reassigned each time the constructor is called (that is, each object is created).
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
this.getName = function() {
return this.name;
};
this.getMessage = function() {
return this.message;
};
}
Copy the code
In the above code, we don’t take advantage of closures, so we can avoid them. Modify it as follows:
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString(); }MyObject.prototype = {getName: function() {
return this.name;
},
getMessage: function() {
return this.message; }};Copy the code
You can do that
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