在了解闭包之前先得弄清楚之前讲的作用域的相关知识。
1、什么是闭包
可以在外部通过某一种手段(方法)可以访问到内部的变量,这种方法就叫做闭包。
2、闭包用法的简单举例
function fun1(){ var a = 100; } fun1()
console.log(a);
如上代码所示,依我们所学的知识我们此时想要打印出a的值应该不行的,因为a定义的作用域在函数内,全局的GO上是没有a这个变量的,故会报错。
那么我们怎么样才能让它在外部打印出来呢
function fun1(){ var a = 100; function fun2(){ console.log(a); } return fun2; } var fun = fun1(); fun()
如上所示,我们先将我们所要执行的代码封装在一个函数内部的另一个函数里,然后返回这整个函数并在外部接受然后触发,这样就能做到我们想要的效果。
那么,原理是什么呢?
首先我们分析一下函数的作用域:
在fun1执行时,生成fun1所对应的作用域,fun1.AO = {a:100,fun2:function}(此处省略分析过程)
在fun2被返回执行的时候,生成fun2所对应的作用域,fun2.AO = { }
这时关键点在于,返回出来的fun2这整个函数就算返回到了fun1外面,但是fun2的父级依然还是fun1,那么在fun2的AO中找不到a对应的值,就返回父级的AO中寻找其所对应的值,而fun1的AO中有a,值为100。
所以这是fun2被返回调用后执行打印出来的结果就是100。
3、那么我们再看一个情况:
function fun1(){ var a = 100; function fun2(){ a ++; console.log(a); } return fun2; } var fun = fun1(); fun() fun()
如上所示,根据之前的知识,我们知道这里当fun第一次调用时打印的结果为101,那么当fun第二次调用的时候呢?
因为fun2的AO中并没有定义a,所以fun2调用时改变的a的值是在其父级也就是fun1的AO中改变a的值,所以当fun第二次被调用的时候,此时fun1中a的值已经变为101,故第二次调用完后打印出来的值为102。
4、这里我们再讨论一种做法:
<ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> <script type="text/javascript"> var lis = document.getElementsByTagName("li"); for(var i = 0;i < lis.length;i++){ lis[i].onclick = function(){ console.log(i) } } </script>
如上所示,我们想要点击某一个数时后台打印出其对应的数字,粗略来看,上述逻辑好像并没有什么问题,但不管点击什么打印出来都只是10.
那么这是为什么呢?
因为在我们点击之前,全局已经生成了作用域GO,此时因为循环已经完成,生成的GO中变量i的最后赋值肯定是为10,然而当我们点击触发时间函数时所生成的作用域AO中没有关于i的定义,只能返回父级寻找,所以这时找到的i值就为10.
那么怎么解决呢?
<ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> <script type="text/javascript"> var lis = document.getElementsByTagName("li"); for(var i = 0;i < lis.length;i++){ (function(i){ lis[i].onclick = function(){ console.log(i) } })(i) } </script>
如上所示,将点击事件函数装在一个立即执行函数中,形参实参都为i,这时每一次循环所生成的函数参数都为列表所对应的值,那么这所生成的所有函数的AO中都会有其所对应的i值,而这些函数正是我们点击事件函数的父级,所以这时我们再去点击触发时返回父级所寻找的i值即为我们想要的值。
以上简单的列举了闭包的一些简单用法,闭包具体的应用还有很多很多,学无止境。