This article describes the principles and usage of JavaScript closures. Share it for your reference, as follows:
1. Two concepts related to closures:
1) Scope of variables
Variables without the keyword var will become global variables;
Use keywords in functionsvar
The declared variable is a local variable.
Local variables can only be accessed inside the function, but cannot be accessed outside the function. However, within the function, you can search upward through the scope chain until the global object, that is, you can access variables outside the function inside the function.
2) The survival cycle of variables
For global variables, their survival cycle is permanent unless the global variable is actively destroyed;
For using keywords in functionsvar
Declared local variables, when exiting the function, these local variables are destroyed as the function call ends.
var func = function() { var i = 1; alert(i); // Output: 1}; alert(i); // Error: i is not defined.
Exception: Closure
var func = function() { var i = 1; return function() { alert(i); i++; } }; var f1 = func(); f1(); // Output: 1f1(); // Output: 2var f2 = func(); f2(); // Output: 1f2(); // Output: 2
2. Start with a classic application of closure
<div>0</div> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <script> var divs = ("div"); for (var i = 0; i < ; i++) { divs[i].onclick = function() { alert(i); }; } </script>
Question: No matter which div is clicked, 5 will pop up.
Reason: The onclick event is triggered asynchronously. When the event is triggered, the for loop has already ended, and the value of variable i is already 5.
Solution: With the help of closure, close the i value of each loop. When the event function looks for variable i from the inside to the outside along the scope chain, it will first find i that is enclosed in the closure environment. When clicking the div, 0, 1, 2, 3, 4 will be output respectively.
<div>0</div> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <script> var divs = ("div"); for (var i = 0; i < ; i++) { divs[i].onclick = (function(num) { return function() { alert(num); }; })(i); } </script>
Similar example: closure is assigned to the array directly
function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } for (var i = 0; i < 10; i++) alert(createFunctions()[i]());
Result: Each element of result returns 10.
Note: The scope chain of closures has obvious side effects - closures always obtain the final value of external function variables. In the above code, the external function produces an array of functions result and returns. Each element in the function array is a function, and each function returns an i variable. It seems that each function should return the i value of each loop, i.e. return 0 to 9 in turn, but the fact is that the return result of each function is 10. This is because each internal function returns the variable i, not the specific value of i at a certain moment, and the scope of i is the entire external function. When the external function is executed, the value of i is 10.
Solution: Inside each internal function, another anonymous function is generated and returned.
function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = (function(num) { return function() { return num; }; })(i); } return result; } for (var i = 0; i < 10; i++) alert(createFunctions()[i]());
Result: result returns 0 to 9 in turn.
Description: (i) Make this layer anonymous function execute immediately.
3. Closure
Sometimes you need to get local variables in the function. How to read local variables from outside? That is to define another function inside the function.
Closures refer to functions that have access to variables in another function scope. The most common way to create closures is to create another function within one function and access local variables of this function through another function. The closure can break through the action chain domain and pass variables and methods inside the function to the outside.
① The principle of closure
1) In the background execution environment, the scope chain of the closure contains its own scope, function scope and global scope.
2) Usually, the scope and variables of a function are destroyed after the function execution is finished.
3) However, when the function returns a closure, the scope of the function will be saved in memory until the closure does not exist.
② Characteristics of closures
1) Nested functions within the function.
2) Internal functions can refer to outer parameters and variables.
3) Parameters and variables will not be recycled by the garbage collection mechanism.
③ The purpose of closure
1) Read variables inside the function.
function f1(){ var n = 999; function f2(){ alert(n);//999 } }
In the above code, function f2 is included inside function f1, and all local variables inside f1 are visible to f2. But the other way around is not possible. Local variables inside f2 are invisible to f1. Since f2 can read local variables in f1, as long as f2 is used as the return value, it can read its internal variables outside f1.
function f1(){ var n = 999; function f2(){ alert(n); } return f2; } var result=f1(); result();//999
A closure is a function that can read variables inside other functions. Since in Javascript language, only subfunctions inside functions can read local variables, closures can be simply understood as "functions defined inside a function". So, in essence, a closure is a bridge connecting the inside and the outside of the function.
function f1(){ var n = 999; nAdd = function(){n += 1} function f2(){ alert(n); } return f2; } var result=f1(); result();//999 nAdd(); result();//1000
result is actually the closure f2 function. It runs twice in total, the first value is 999 and the second value is 1000. This proves that the local variable n in function f1 has been kept in memory and is not automatically cleared after f1 is called. The reason is that f1 is the parent function of f2, and f2 is assigned to a global variable, which causes f2 to always be in memory, and the existence of f2 depends on f1. Therefore, f1 is always in memory and will not be recycled by the garbage collection mechanism after the call is finished.
2) Make the value of variables inside the function always remain in memory (extend the life of local variables).
var print, add, set; function closure() { var number = 8; print = function() { alert(number); } add = function() { number++; } set = function(x) { number = x; } } closure();//Create a closureadd(); print();//9 set(0); print();//0 var oldClosure = print; closure();//Create a new closureprint();//8 oldClosure();//0
Notes when using closures: Since closures will cause all variables in the function to be stored in memory, which consumes a lot of memory, closures cannot be abused, otherwise it will cause performance problems of the web page and may lead to memory leakage in IE. The solution is to delete all local variables that are not used before exiting the function. In other words, closures will refer to the scope of external functions and occupy more memory. Overuse of closures will lead to performance problems. So, use closures only if necessary. For functions that generate closures, they should be dereferenced after use.
3) Self-executing functions + closures to reduce global variable pollution (encapsulate private variables)
var person = (function() { var_name = "Alice"; var _id = 16; return { getUserInfo: function() { return _name + ": " + _id; } } })();
Use underscores to agree on private variables _name and _age, which are encapsulated in the scope generated by the closure, and these two variables cannot be accessed externally, which avoids global command pollution.
④ Disadvantages of closures:
1) Additional scopes are required.
2) Transition to use closures will take up a lot of memory.
4. This object
Using this object inside a closure will produce some complex behavior. The value of this object is determined at runtime based on the execution environment where the function is located: when used in a global function, this is equal to window (non-strict mode) or undefined (strict mode); and when called as an object method, this is equal to this object.
var name = "The window"; var object = { name: "My object", getName: function() { return function() { return ; }; } }; alert(()());//Output: "The window"
Once each function is called, it will automatically obtain this and arguments variables. An internal function cannot directly access these two variables from an external function. This problem can be solved by storing this object in another variable. Save this object in the external scope in a variable that can be accessed by a closure, and the closure can access the object.
var name ="The window"; var object = { name: "My object", getName: function() { var that = this; return function() { return ; }; } }; alert(()());//Output: "My object"
To have the closure access this and arguments objects of external functions, it can be done by storing their references in another variable.
5. Memory leak
When using closures, it is easy to cause circular references. If the scope of the closure contains some DOM nodes, it may cause memory leakage. But in fact, this is not a closure problem, but because: the objects in BOM and DOM are implemented in the form of COM objects using C++, and the garbage collection mechanism of COM objects adopts a reference counting strategy. In the garbage collection mechanism based on the reference counting strategy, if a circular reference is formed between the two objects, neither of these objects can be recycled.
function assignHandler() { var element = ("id"); = function() { alert(); } }
An anonymous function saves a reference to an element. As long as the anonymous function exists, the number of references to the element is at least 1, and the memory it occupies will never be recycled.
function assignHandler() { var element = ("id"); var id = ; = function() { alert(id); } element = null; }
ByA copy of , is stored in a variable, and referencing the variable in the closure eliminates the circular reference, but doing this alone cannot solve the memory leak problem. The closure will refer to all active objects containing the function, including element. Even if the closure does not directly refer to element, a reference will still be saved in the active object containing the function. Therefore, it is necessary to set the element element to null, so that the reference to the DOM object can be dereferenced and the memory it occupies is normally recycled.
6. Imitate block-level scope
There is no direct block-level scope in JavaScript.
function output(count) { for (var i = 0; i < count; i++) { alert(i); } alert(i);//Output: 10}
Using closures can mimic the block-level scope—create and call a function immediately, so that the code inside it can be executed without leaving a reference to the function in memory. The result is that all variables of the internal function are destroyed immediately unless some variables are assigned to variables in the included scope (i.e., external scope).
Anonymous functions used as block-level scope: Include a function declaration in a pair of parentheses, indicating that it is actually a function expression, and another pair of parentheses that are immediately called the function.
(function() { //This is the block-level scope;}) ();
Anonymous function expressions can be used to simulate block-level scope. Any variables defined in anonymous functions will be destroyed after the anonymous function is executed. Accessing these variables outside the anonymous function will cause an error.
function output(count) { (function() { for (var i = 0; i < count; i++) { alert(i); } }) (); alert(i);//An error occurred}
For more information about JavaScript, please view the special topic of this site: "JavaScript object-oriented tutorial》、《Summary of common JavaScript functions techniques》、《Summary of JavaScript Errors and Debugging Skills》、《Summary of JavaScript data structure and algorithm techniques》、《Summary of JavaScript traversal algorithm and skills"and"Summary of JavaScript mathematical operations usage》
I hope this article will be helpful to everyone's JavaScript programming.