作为向我一样的前端小白来说,闭包是一个比较难懂的地方。但是它在js中确实相当的重要,对于提高写js的水平也有很大帮助。前几天通过园子里的文章和群里大神的讲解,对闭包有了一定的了解。于是在这里做一下复习,有什么不对的地方,希望各位指正。
闭包的概念
由于官方给出的概念实在有些难懂,就不写了。简单的来说,在函数内再定义一个函数,就产生了一个闭包。
闭包的作用
为什么要在函数里再定义一个函数呢?有什么作用呢?这个就要牵扯的js的作用域链问题了。
1 window.a=111; 2 3 function test(){ 4 alert(a); 5 } 6 7 test();//111
在window下定义一个a=111和函数test(),这里产生了2个作用域,一个事根作用域,也就是window对应的作用域,另一个是函数test的作用域,其中test的作用域是window作用域的子节点。当在test里alert(a)时,现在test自身的作用域内查找a,由于a没有定义,于是向上一级作用域查找,知道根作用域(这里直接是根作用域),未找到是返回undefined。也就是说,在子作用域里可以访问祖先作用域里的变量。那么如果先要在祖先作用域里访问子作用域的变量应该怎么办?这个就是闭包的作用了,允许我们在祖先作用域里访问子作用域的变量。
1 function test(){ 2 var a=1; 3 } 4 5 console.log(a);//undefined 6 7 //在正常情况下直接在全局里alert(a)是不行的 8 //为了访问这个a,我们可以在test函数里再定义并且返回一个函数, 9 10 function test(){ 11 var a=1; 12 13 return function(){ 14 console.log(a); 15 } 16 } 17 18 var a=test(); 19 a();//1
在第10行的test函数里,我们返回了一个匿名函数用于打印a,当我们在window作用域下调用时,能打印出a,因为这个匿名函数也产生了一个作用域,并且挂在了test作用域之下,所以我们能够访问到a。
我们再拿一个在讲解闭包时用的比较多的例子来看一下。
1 var result=[]; 2 3 function test(){ 4 for(var i=0;i<3;i++){ 5 result[i]=function(){console.log(i)}; 6 } 7 } 8 9 test(); 10 result[0]()//3 11 result[1]()//3 12 result[2]()//3
在这里result[0],result[1],result[2]执行时打印的都是3,为什么会这样,我们来画个图解释一下
在执行完test()之后会有这样一个情况,作用域链上有3个对象,分别是window的作用域,test的作用域和匿名函数的作用域。而数组result的3个元素都指向匿名函数。当执行result[0]()时,匿名函数开始在自身作用域查找i,因为没有定义,于是向上级test作用域查找,在test作用域里,i经过for循环,已经变成了3,所有result[0],result[1],result[2]都等于3。那么我们想得到0,1,2应该怎么办呢?我们可以在test里面再定义一个函数,如代码:
1 var result=[]; 2 3 function test(){ 4 5 for(var i=0;i<3;i++){ 6 result[i]=(function(num){ return function(){console.log(num)} })(i); 7 } 8 } 9 10 test(); 11 result[0]();//0 12 result[1]();//1 13 result[2]();//2
这里又发生了什么事呢?首先执行完test()之后,result[0],result[1],result[2]都指向匿名函数function(){console.log(num)}。当他们执行的时候,依旧是查找num,在上一级匿名函数中找到了参数num,这个num是多少呢?是在上级函数test()执行时赋值的i,因此,这里的num分别为0,1,2。于是就有了最后我们希望得到的结果。在解释里,我们好像并没有提到闭包。这正是我希望的,因为我们需要的是掌握这样一个原理,而不是一个概念。闭包那个生涩的概念反而会阻碍我们更好的理解。
好了,这篇就到这里了。希望各位看官能看懂… 本人的表达能力确实不太好…
原文:http://www.cnblogs.com/nicoBlog/p/5060711.html