何为闭包?
在我的理解看来,闭包说白了就是一个函数,它可以引用函数外部的变量,当然这里的外部其实是一个作用域(也可以理解成为另一个函数)。如果这个作用域为函数作用域的话,即使外层函数已经执行完毕,但是里面被内层函数引用的变量依旧存储在内存中,除非内层函数已经执行完毕。
闭包原理
说到闭包,就不得聊一聊作用域的问题了,举个栗子:
function testClosure(propertyName){return function(obj1,obj2){var p1=obj1['propertyName'];var p2=obj2['propertyName'];if(p1>p2){return 1;}else if(p1<p2){return -1;}else{return 0;}}}var result=testClosure('name');result({name:'mickey'},{name:'bob'});
ok,这其实就是一个简单的闭包,要特别注意匿名函数中的 var p1=obj1['propertyName']和var p2=obj2['propertyName'];仔细发现这里引用了外层函数的变量propertyName, 即使这个内部函数被返回了,而且是在被其他地方调用了,但他仍然可以访问变量propertyName,之所以可以访问这个变量,是因为内部函数的作用域链中包含testClosure()的作用域。关于作用域链,如下一张图:

用闭包解决实际问题
案例 1
function testClosure(){var result=new Array();for(var i=0;i<10;i++){result[i]=function(){return i;};}return result;}
如上,在执行testClosure函数的时候,result其实是一个函数数组,返回值是每项值为10的数组,而非想象中的那样返回值为1…10,为了解决以上问题,我们可以这样去实现
解决方案 1
function testClosure(){var result=new Array();for(var i=0;i<10;i++){result[i]=function(num){return function(){return num;}}(i);}return result;}
如上解决方法是利用立刻执行函数和闭包的特性将每一次for循环的变量i传入自执行的匿名函数中,在自执行函数中,利用闭包的特性,返回一个函数,这个函数就是我们result数组中的函数,然后在函数中通过return一个num来实现返回一个值,这样,每一个for循环在进行迭代的时候,i的值都被保存在自执行函数中,即使自执行函数执行完就销毁,但是里面的变量由于被内部函数引用,所以会一直停留在内存中。
解决方案 2
function testClosure(){var result=new Array();for(let i=0;i<10;i++){result[i]=function(){return i;};}return result;}
这里不仔细看的话,会发现和之前的源代码没有太大区别,但是在for循环里面,我们使用ES6中的let关键字取代了var,ES6中的let为JavaScript引入了块级作用域的概念,这就说明每一次for循环进行迭代的时候,i的值都只为每一个迭代中的{ }服务,相当于每一次迭代只维护一个{ },在C++或者JAVA中,如果直接在for循环里面声明i变量的话,它是默认具有块级作用域的。但是var不行,var即使在里面申请也会被默认看成是全局的(相对于一个函数作用域的全局)
案例 2
我们常常在为按钮绑定事件的时候,正常情况下,直接一个button.onclick=function(){ },其实就完事儿了,但是对于多个按钮呢?而且我还想实现点击的时候区别到底是哪一个按钮被触发了呢?如下:
<body><button class="button">按钮1</button><button class="button">按钮2</button><button class="button">按钮3</button><script type="text/javascript">let buttons=document.getElementsByClassName("button");for(var i=0;i<buttons.length;i++){buttons[i].onclick=function(){console.log(i+1);};}</script></body>
这里很遗憾的是,当我们点击任何一个按钮,控制台都只会输出5。
解决方案
<body><button class="button">按钮1</button><button class="button">按钮2</button><button class="button">按钮3</button><script type="text/javascript">let buttons=document.getElementsByClassName("button");for(var i=0;i<buttons.length;i++){buttons[i].onclick=function(num){return function(){console.log(num+1); }}(i);}</script></body>
原理和上个案例类似,不在过多赘述,第二种解决方案也是使用let替换var.
案例 3
详情请点击JS中的闭包理解
