对于闭包的理解

学了这么久的javascript一直没搞懂闭包,今天又看了一遍终于搞懂了。

闭包即在包含在函数中的函数,要创建闭包,只要在一个函数中再创建一个函数或者将一个函数作为返回值返回。由于这个函数在另一个函数内部,所以它可以访问外部函数中的上下文环境,例如:

函数fn中有一个匿名函数,匿名函数接收一个变量,并返回这个变量与fn中a的值相加的结果。当变量p调用fn时,即将fn中的匿名函数赋值给p,当调用函数p时,传入的参数是5,即s=5,而p()中并没有定义变量a,所以函数p只能沿着作用域链去访问它创建时所处的上下文环境中寻找变量a,而它被创建的上下文环境即匿名函数被创建的地方,即fn内部,于是在fn内部找到了变量a并使用,所以最终结果为9。这里有一点要注意,当一个内部函数中使用了一个自身不存在的变量时,并不是去它的“父”级作用域寻找,而是从创建它的那个上下文中寻找;如果是前者的话,在上面这个例子中添加一个全局变量a值为6:

结果还是一样,证明后者才是更确切的说法;

由于闭包的这种特性也就带来了一个问题,正常情况下,当函数被调用完时,它的上下文即被出栈并销毁,但是如上面例子中,当fn调用完毕后,它的上下文并没有销毁,因为匿名函数赋值给了p,随意p将它的作用域保存了下来,而匿名函数中需要使用到fn中的变量,所以fn的上下文不允许被销毁,这就会产生一定的性能损失;

还有一个就是闭包在访问外部函数的变量时,是直接使用而非复制,因为变量无法被销毁,即在闭包内部使用的和外部的是同一个值

来看这个例子,fn中有另一个函数add,add中使用了fn中的变量i,没调用一次add,i自增一次;如果这个还不明显的话,那就再看一个经典的问题:

按照常理,这最后的结果应该是打印出0~9,但结果却全是10,这就验证了上面的说法,rusult中每个匿名函数中使用的i与createFunctions中的i是同一个值,就相当于是引用createFunctions中的i,由于数组中每一项都是保存一个匿名函数,并不是保存的匿名函数的执行结果,所以当对数组中每一项进行调用时,在createFunctions中,i已经为10了,所以输出结果都为10,如果改一下:

在for循环中,rusult每项都直接获取匿名函数的值,结果就和预料的一样了,这是因为在这个匿名函数被执行时,i的值刚变化就被输出了,而不立马执行只是保存i的引用,循环结束后i的值变为10,所以数组中保存的值也全为10;

原文地址:https://www.cnblogs.com/cjw-ryh/p/6882511.html