一、定义
闭包就是能够读取其他函数内部变量的函数。我们知道js的作用域特点是外层函数无法读取内层函数的变量,但是内层函数可以读取外层函数的变量,所以这要求闭包一定要在其他函数内部。所以闭包可以定义为“在一个函数内部的函数”。
1 | function outer(){ |
二、特征
闭包有两个很重要的特性
- 可以读取父函数内部的变量
- 可以在父函数执行完之后还保留其内部变量
- 在定义时只取得变量的引用,在执行时才会实际取值
第1点很容易理解,因为js的作用域的特点赋予闭包能访问父函数的内部变量。关于第2点,通常我们定义闭包之后不会立即执行,而是将其返回,等待合适的时机再执行。
1 | function outer(){ |
上面的例子,我们在父函数outer内部定义了一个闭包inner,在执行outer函数后,将get_n指向inner函数,当在外层执行get_n函数也就是inner函数时,才返回outer函数内部定义的n。
三、应用场景
根据闭包的特征,我们可以做到在函数外部取到内部的变量值,因此我们可以将某些不想要定义到全局的变量,用闭包来实现,类似于“私有变量”。
1 | function outer(){ |
上面的代码,封装了一个“私有变量”n,在外部无法直接读写,但是可以通过get_n和set_n两个函数来读写。
四、特别关注
闭包很常见的一个问题是
1 | function outer(){ |
上面的代码将会在控制台打印出3行3。这个其实很好理解,当执行“var o = outer();”这行代码时,得到的o这个数组里有三个元素,都是一个用来打印出i的函数。从上面闭包的第3个特征知道,定义这3个函数的时候,并没有取得i的值,只是保存了对i的引用,在实际执行前,i的值已经经过循环后变成了3,所以再执行返回出来的方法,都将打印出3。
所以尽量不要在闭包里使用循环。不过还是有办法解决这个问题的。
1 | function outer(){ |
上面的代码将会分别打印0,1,2。和上一个例子相比,只是在闭包外多嵌套了一层立即执行的函数,在该函数执行的时候取到的值是当时的值,而内部的闭包取到的是立即执行的函数内的变量的引用,也就是当时的i(相当于复制了一个当时的i,已经不是outer内部定义的那个i的引用了),我们可以看看的代码的执行过程。
1 | var result = []; |
参考资料:带你一分钟理解闭包–js面向对象编程