Introduction
A closure is a function that has permission to access variables in another function scope.
Closures are difficult to understand in JavaScript. Many advanced applications rely on closures to implement them. Let’s first look at an example below:
function outer() { var i = 100; function inner() { console.log(i); }}In the above code, according to the scope of the variable, all local variables in the function outer are visible to the function inner; local variables in the function inner are invisible outside the function inner, so local variables in the function inner cannot be read outside the function inner.
Since the function inner can read the local variables of the function outer, as long as the inner is used as the return value, the inner local variables can be read directly outside the ouer.
function outer() { var i = 100; function inner() { console.log(i); } return inner;}var rs = outer();rs();This function has two characteristics:
After executing var rs = outer() in this way, the actual rs points to the function inner. This code is actually a closure. That is to say, when the function inner inside the function outer is referenced by a variable outside the function outer, a closure is created.
Scope
Simply put, scope is the accessible range of variables and functions, that is, scope controls the visibility and life cycle of variables and functions. In JavaScript, the scope of variables is global and local.
Global scope
var num1 = 1;function fun1 (){ num2 = 2;}The above three objects num1, num2 and fun1 are all global scopes. It should be noted here that the variables that define direct assignments at the end are automatically declared as having global scopes;
Local scope
function wrap(){ var obj = "I'm wrapped up by wrap, and the outside of wrap cannot directly access me"; function innerFun(){ //The outside cannot access me}}Scope chain
Everything in Javascript is an object. These objects have a [[Scope]] property, which contains a collection of objects in the scope created by the function. This collection is called the Scope Chain of the function, which determines which data can be accessed by the function.
function add(a,b){ return a+b;}When a function is created, its [[scope]] property will automatically add the global scope
var sum = add(3,4);
When a function is called, an internal object called the execution context is created. This object z defines the environment when the function is executed. It also has its own scope chain for identifier resolution, and its scope chain is initialized as an object contained in [[Scope]] of the current running function.
During function execution, every time a variable is encountered, an identifier parsing process will be passed to decide where to get and store data. This process starts from the head of the scope chain, that is, searches for an identifier of the same name from the active object. If it is found, use the variable corresponding to this identifier. If it is not found, continue to search for the next object in the scope chain, if all objects are searched (the last one is a global object) are not found, the identifier is considered undefined.
Closure
A closure is simply a function that accesses its external variables.
var quo = function(status){ return { getStatus: function(){ return status; } }}status is saved in quo, it returns an object, the method getStatus in this object refers to the status variable, that is, the getStatus function accesses its external variable status;
var newValue = quo('string');//Return an anonymous object, referenced by newValue with newValue.getStatus();//Accessed the internal variable status of quoIf the getStatus method is not available, then status will be automatically recycled after quo('sting'). It is precisely because the returned anonymous object is referenced by a global object, and the anonymous object depends on status, so it will prevent the release of status.
example:
//Error scheme var test = function(nodes){ var i ; for(i = 0;i<nodes.length;i++){ nodes[i].onclick = function(e){ alert(i); } }}An anonymous function creates a closure, and the i it accesses is i in the external test function, so each node actually refers to the same i.
//Improvement solution var test = function(nodes){ var i ; for(i = 0;i<nodes.length;i++){ nodes[i].onclick = function(i){ return function(){ alert(i); }; }(i); }}Each node is bound to an event. This event receives a parameter and runs immediately, passing in i. Because it is passed by value, each loop will generate a new backup for the current i.
The role of closure
function outer() { var i = 100; function inner() { console.log(i++); } return inner;}var rs = outer();rs(); //100rs(); //101rs(); //102In the above code, rs is the closure inner function. rs ran three times in total, the first time was 100, the second time was 101, and the third time was 102. This shows that the local variable i in the function outer has been kept in memory and was not automatically cleared when called.
The purpose of the closure is that after the outer execution is completed and returned, the closure makes the JavaScript's garbage collection mechanism (grabage collection) not recycle the memory occupied by the outer, because the execution of the inner function of the outer depends on the variables in the outer. (Another explanation: outer is the parent function of inner, the inner is assigned to a global variable, causing inner to be in memory all the time, and the existence of inner depends on outer, because some outer is always in memory and will not be garbage collected and recycled after the call is finished).
The closure has permission to access all variables inside the function.
When a function returns a closure, the scope of the function will be saved in memory until the closure does not exist.
Closures and variables
Due to the scope chain mechanism, the closure can only get the last value containing any variable in the function. See the following example:
function f() { var rs = []; for (var i=0; i <10; i++) { rs[i] = function() { return i; }; } return rs;}var fn = f();for (var i = 0; i < fn.length; i++) { console.log('Function fn[' + i + ']() Return value:' + fn[i]());}Functions will return an array. On the surface, it seems that each function should return its own index value. In fact, each function returns 10. This is because the scope chain of the first function contains the active objects of function f, and they refer to the same variable i. When function f returns, the value of variable i is 10. At this time, each function saves the same variable object of variable i. We can force the closure to behave as expected by creating another anonymous function.
function f() { var rs = []; for (var i=0; i <10; i++) { rs[i] = function(num) { return function() { return num; }; }(i); } return rs;}var fn = f();for (var i = 0; i < fn.length; i++) { console.log('Function fn[' + i + ']() Return value:' + fn[i]());}In this version, instead of assigning the closure to the array directly, we define an anonymous function and assign the result of immediately executing the anonymous function to the array. Here, the anonymous function has a parameter num. When calling each function, we pass in the variable i. Since the parameters are passed by value, the variable i will be copied to the parameter num. Inside this anonymous function, a closure accessing num is created and returned. In this way, each function in the rs array has a copy of its own num variable, so different values can be returned.
This object in closure
var name = 'Jack';var o = { name : 'bingdian', getName : function() { return function() { return this.name; }; }}console.log(o.getName()()()); //Jackvar name = 'Jack';var o = { name : 'bingdian', getName : function() { var self = this; return function() { return self.name; }; }}console.log(o.getName()()()); //bingdianMemory leak
function assignHandler() { var el = document.getElementById('demo'); el.onclick = function() { console.log(el.id); }}assignHandler();The above code creates a closure as the el element event handler, and this closure creates a circular reference. As long as the anonymous function exists, the number of el references is at least 1, because the memory it occupies will never be recycled.
function assignHandler() { var el = document.getElementById('demo'); var id = el.id; el.onclick = function() { console.log(id); } el = null;}assignHandler();Setting the variable el null can dereference the DOM object and ensure that it consumes memory normally.
Imitate block-level scope
The statement set in any pair of curly braces ({and}) belongs to a block, and all variables defined in this are invisible outside the code block, which we call block-level scope.
(function(){ //Block-level scope})();Application of closures
Protect the security of variables in the function. As in the previous example, only the function inner can access i in the function outer, but cannot be accessed through other channels, thus protecting the security of i.
Maintain a variable in memory. As in the previous example, due to the closure, the i in the function outer always exists in memory, so every time rs() is executed, i will be added 1.