闭包函数:声明在一个函数中的函数,叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
闭包是指一个函数,其可以调用另一个函数作用域中的变量;
闭包只能取得包含函数中任何变量的最后一个值,如:
实例1
function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ result[i] = function(){ return i; }; } return result; } var funcs = createFunctions(); for (var i=0; i < funcs.length; i++){ console.log(funcs[i]()); //10 10 10 10 10 10 10 ...
问题解决:可以通过创建另一个匿名函数,强制让闭包的行为符合预期。
function createFunctions() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function(num) { return function() { return num; }; }(i) } return result; } var funcs = createFunctions(); for (var i = 0; i < funcs.length; i++) { console.log(funcs[i]()); //0 1 2 3 4 5 6 7 8 9 }
实例2
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <ul> <li>click me</li> <li>click me</li> <li>click me</li> <li>click me</li> </ul> <script type="text/javascript"> var elements=document.getElementsByTagName('li'); var length=elements.length; for(var i=0;i<length;i++){ elements[i].onclick=function(){ alert(i); } } </script> </body> </html>
因为闭包,点击每一个,弹出的结果都为4;
可以通过创建另一个匿名函数,强制让闭包的行为符合预期。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS 闭包</title> </head> <body> <ul> <li>hello 1</li> <li>hello 2</li> <li>hello 3</li> <li>hello 4</li> </ul> <script type="text/javascript"> var elements = document.getElementsByTagName('li'); var length = elements.length; for (var i = 0; i < length; i++) { elements[i].onclick = function(num) { return function() { alert(num); } }(i) } </script> </body> </html>
或者
var lis = document.getElementsByTagName("li"); for (var i = 0; i < lis.length; i++) { (function(i) { lis[i].onclick = function() { console.log(i); }; })(i); }
或者
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <ul> <li>click me</li> <li>click me</li> <li>click me</li> <li>click me</li> </ul> <script type="text/javascript"> var elements=document.getElementsByTagName('li'); var length=elements.length; var handler = function(i){ return function (){ alert(i); } } for(var i=0;i<length;i++){ elements[i].onclick= handler(i); } </script> </body> </html>
下面为什么会立即执行呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <ul> <li>click me</li> <li>click me</li> <li>click me</li> <li>click me</li> </ul> <script type="text/javascript"> var elements=document.getElementsByTagName('li'); var length=elements.length; var handler = function(i){ // return function (){ alert(i); // } } for(var i=0;i<length;i++){ elements[i].onclick= handler(i); } </script> </body> </html>
闭包的作用:
1. 可以读取函数内部的变量
2. 让这些变量的值始终保持在内存中。
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
function f1(){ n=999; function f2(){ alert(n); // 999 } }
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1 就是不可见的。这就是Javascript语言特有的“链式作用域”结构(chain scope),
子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
function f1(){ n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是“nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此 nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个
匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
还没懂?再看看https://blog.csdn.net/weixin_43586120/article/details/89456183