Closures - Everywhere
In front-end programming, the use of closures is very common. We often use closures intentionally or unintentionally, directly or indirectly. Closures can make data transfer more flexible (such as handling some click events)
!function() { var localData = "localData here"; document.addEventListener('click', //处理点击事件时用到了外部局部变量,比如这里的localData function(){ console.log(localData); }); }();
Another example is the following: (Isn’t it very friendly~~)
!function() { var localData = "localData here"; var url = "http://www.baidu.com/"; $.ajax({ url : url, success : function() { // do sth... console.log(localData); } }); }();
Let’s look at another example~~This situation is what we usually call Closure
function outer() { var localVal = 30; return function(){ return localVal; } } var func = outer(); func(); // 30
In this example, calling outer() returns the anonymous function function(). This anonymous function can access the local variable localVal of outer(). After the outer() call is completed, when func() is called again, the The local variable localVal of outer() can be accessed
The concept of closure
Closure, unlike ordinary functions, allows a function to still access non-local variables when called outside the immediate lexical scope. --Wikipedia
A closure is a function that can read the internal variables of other functions. --Ruan Yifeng
Since in the Javascript language, only sub-functions inside the function can read local variables, closures can be simply understood as "functions defined inside a function".
So, in essence, closure is a bridge that connects the inside of the function with the outside of the function
Uses of closures
This part is reproduced from this blog post
Closures can be used in many places. It has two greatest uses. One is to read the variables inside the function as mentioned earlier, and the other is to keep the values of these variables in memory.
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
In this code, result is actually the closure f2 function. It was run twice, the first time the value was 999, the second time the value was 1000. This proves that the local variable n in function f1 is always stored in memory and is not automatically cleared after f1 is called.
Why is this happening? 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, so f1 is always in memory and will not be deleted after the call is completed. , recycled by the garbage collection mechanism (garbage collection).
Another thing worth noting in this code is the line "nAdd=function(){n+=1}". First of all, the var keyword is not used before nAdd, so nAdd is a global variable, not a local variable. . Secondly, the value of nAdd is an anonymous function, and the anonymous function itself is also a closure, so nAdd is equivalent to a setter, which can operate on local variables inside the function outside the function.
Closure - Encapsulation
(function() { var _userId = 23492; var _typeId = 'item'; var export = {}; function converter(userId) { return +userId; } export.getUserId = function() { return converter(_userId); } export.getTypeId = function() { return _typeId; } window.export = export; //通过此方式输出 }()); export.getUserId(); // 23492 export.getTypeId(); // item export._userId; // undefined export._typeId; // undefined export.converter; // undefined
Using the characteristics of closure allows us to encapsulate some complex function logic. In this example, calling the method on export (getUserId, getTypeId) indirectly accesses the private variables in the function, but calls export directly. _userId cannot get _userId. This is also a commonly used feature in Node~
Common mistakes: loop closure
In the following case, we add 3 divs with values aaa, bbb, ccc. What we want to achieve is to click aaa to output 1, and click bbb Output 2, click ccc to output 3
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div><div id=div3>ccc</div>"; for (var i = 1; i < 4; i++) { document.getElementById('div' + i). addEventListener('click', function() { alert(i); // all are 4! }); }
As a result, click aaa, bbb or ccc are all alert(4)~~
The problem is that the value of i is already 4 when the initialization is completed
To achieve what we want, click aaa to output 1, click bbb to output 2, and click ccc to output 3. We need to use the closure technique. In each loop, wrap it with an anonymous function that is executed immediately, like this If you do this, the value of alert(i) each time will be taken from i in the closure environment. This i comes from the assignment i of each loop and it will output 1, 2, 3
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div>" + "<div id=div3>ccc</div>"; for (var i = 1; i < 4; i++) { !function(i){ //②再用这个参数i,到getElementById()中引用 document.getElementById('div' + i). addEventListener('click', function() { alert(i); // 1,2,3 }); }(i); //①把遍历的1,2,3的值传到匿名函数里面 }
Thinking questions
如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。(来自阮老师)这题目总结得真秒~~
代码片段一。
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());
代码片段二。
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());