在计算机科学中,闭包(Closure)是词法闭包(Lexcial Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造他的环境也不例外。所以,另有一种说法认为闭包是自由函数和与其相关的引用环境组合而成的实体。简而言之,闭包就是一个函数和他用到的变量组成的对象。
12 14
上面代码在运行的时候,浏览器显示如下:
由函数 f2 捕获的变量x在函数外面使用的时候依然存在,即函数 f2 和变量x的这个组合便是闭包。
2.闭包是什么时候创建的?在上面代码中,在预处理阶段,函数 f1 被扫描,创建 f1 的词法环境,然后把 f1 里面的 x, y 添加到 f1 的词法环境中,
然后 f2.[[scope]]被创建,并且 f2.[[scope]] == f1.le,
然后扫描 f2 ,发现其用到父函数里面的变量,于是将用到的变量放到一个闭包对象Closure 里面去。
也就是说,在函数 f2 被创建的时候,闭包已经存在。当然,不同的浏览器对闭包对象的处理略有不同。
在JS里面,只有当一个函数捕获其上级函数的变量时才会形成闭包。
还有一点,如下代码所示:
12 16
执行结果为:
即内部函数不一定要返回到外部去被调用才会形成闭包,而是只要被调用就会创建Closure。
并且由调用栈(Call Stack)可以看出 ,函数 f2 被创建的时候,闭包对象被创建,接着函数 f3 被创建,f3 被调用,f2 被调用,然后闭包对象销毁。
所以说,闭包的本质就是词法环境构成的作用域链。
3.闭包的好处 3.1减少全局变量假设有一个重复调用达到累加效果的函数,一般来讲应该是这样的
首先定义一个全局变量,然后对其进行类加,但是,使用闭包之后,神奇的事情发生了:
12 15
没有创建全局变量,依然达到了相同的效果。
该方法将函数f的成员变量a与f1组合为一个闭包对象f,然后将f赋给result,此时在result上a与f1是被绑定的,所以每次调用result,都相当于给redult.a + 1;
12 19
在这个函数中,父函数返回子函数,父函数的形参base和子函数组成了闭包,所以只需要传递一次base参数,就将base绑定到了子函数上,后面就不用再传递base参数,而只需要传递子函数的参数即可。
当给外层函数callFactory()传递不同的参数时,形成不同的闭包对象,真是神奇的写法。
3.3封装例如:使用闭包实现get/set方法的简单封装
12 174.使用闭包的注意点
- 捕获的变量只是个引用,而不是复制。
- 父函数每调用一次,都会产生一个新的闭包,因为函数每次调用都会创建新的词法环境。
- 使用闭包解决循环中的回调函数的异步问题。
有如下代码,给div元素添加点击事件,使之弹出不同的值:
12132435 13
由于for循环中用var声明的变量相当于全局变量,所以在异步的回调函数中执行的时候总是弹出循环的结尾4,而不是需要的1,2,3;
这种情况最简单的解决办法就是使用let而不是var声明变量就OK了,但是在这里还是要用一下闭包,只是为了加深理解。
12132435 15