一、何为闭包

定义

所谓闭包,其实是一个带有执行环境的函数。这个执行环境,就是指 「词法作用域」。那什么叫「词法作用域」 呢?简单说,词法作用域就是代码的书写位置。
闭包,可以在词法作用域的外部执行。这是闭包的特点。还有其他的说法:如,闭包是函数内部定义的函数。如,闭包是有权访问另一个函数内部变量的函数。其实这些都是关于闭包的不完全描述。
《你不知道的JavaScript》 一书中关于闭包的理解:即,可以记住并访问所在词法作用域的函数。再简单点理解就是,在定义函数的作用域之外,能够访问这个作用域内的变量。这样的函数,相当于自己随时带着一个执行环境。这个函数以及它的执行环境,就称之为闭包。下面画图说明。
image.png
整个红框中的变量,以及 sayHello 函数,形成了闭包。包起来的是变量 me。函数 printTxtsayHello 作为执行结果返回了。之后把返回结果赋值给 print。此时,执行 sayHello 函数,当前作用域、整个作用域链中,变量 me 是不存在的。这么操作,应该是找不到 me,并且报错才对。但因为这个闭包的存在, sayHello 函数成功找到变量并执行了。
请注意:js中,作用域链访问权限是由内向外的。也就是说,所有父级作用域对子级可见,但是子级对父级不可见。
到了这里,也就能理解啥 闭包是有权访问另一个函数内部变量的函数了。因为上面的例子表明,就是这么回事。 print 访问到了printTxt 内部的变量。这句没错,但不够严谨。为啥不严谨,看下面的例子:

  1. function printTxt() {
  2. const me = 'Tom'
  3. function sayHello() {
  4. console.log(`This is ${me}!`)
  5. }
  6. return sayHello
  7. }
  8. printTxt()

这里没有产生闭包。 执行 print 能访问到 me ,是因为作用域链的存在。当执行 sayHello 时,首先在自己的作用域内进行了查找,没有找到。但是向上查找,在父级作用域中找到了变量。这种情况,也能够这样描述:sayHello 有权限访问 printTxt 中的变量。但这不是闭包,所以不够严谨。

例子

闭包,其实在代码中随处可见。只是我们没有注意。
以下这些,都是闭包:

  • IIFE(Immediately Invoked Function Expression)立即执行函数表达式
  • 定时器
  • 事件监听器
  • …… 只要使用了回调函数,都会产生闭包。 ```javascript // 无闭包 for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i) // 4, 4, 4, 4, 4 }, 0) }

// 有闭包 for (var i = 0; i < 5; i++) { (function (k) { setTimeout(function () { console.log(k) // 0, 1, 2, 3, 4 }, 0) })(i) }

  1. 如果我们想依次打印 1~5,用第 1 种方式是不行的。因为在定时器执行的时候,访问到的变量 `i` ,是唯一的,只有一个。所以会重复打印。在 2 种方式中,因为IIFE,每次循环都会产生新的作用域,封存了一个变量 kk保留的对 i 的引用。循环结束,并不会立即释放这些变量。一共有5份。 `console.log` 时,会依次次读取每个 IIFE 中的k
  2. <a name="PG1Ii"></a>
  3. ## 二、闭包的用处
  4. 总体来说,常见的用处,大概有以下:
  5. 1. 块级作用域(ES6 let命令,很好的解决这个需求),像上面的 `for` 循环那样
  6. 2. 封装独立模块,避免局部变量、全局变量冲突
  7. 3. 私有变量,私有方法。
  8. <a name="EJ1pP"></a>
  9. ## 三、闭包的缺点
  10. 过度使用闭包,会造成大量的内存地址不能回收,导致内存泄露。
  11. <a name="esmCZ"></a>
  12. ## 四、立即调用函数表达式
  13. IIFE,即 Immediately invoked Function Expression。一个匿名的函数声明,声明之后立即调用。有两种写法,不论哪种,都是表达式后面,紧跟一个 `()`
  14. ```javascript
  15. (function() {
  16. // 一种写法
  17. })()
  18. (function() {
  19. // 一种写法
  20. }())

常见的作用:

  • 闭包
  • 模块化