简单聊聊闭包

98 阅读3分钟

什么是闭包

  • 闭包是指那些能够访问自由变量的函数,这里的自由变量是指外部作用域中的变量,另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。
  • 闭包与JavaScript中的词法作用域函数作用域密切相关。 词法作用域指的是变量的作用域由代码中变量声明的位置决定,而不是由代码执行时的上下文决定。 函数作用域指的是在函数内声明的变量只能在函数内部访问,外部无法直接访问。
  • 内层函数使用外层函数定义的变量来达到不用使用全局变量
  • 闭包是指有权访问另一个函数作用域中变量的函数,优点是私有化数据,在私有化数据的基础上保持数据,缺点使用不恰当会导致内存泄漏,在不需要用到的时候及时把变量置为null

闭包解决了什么问题

就是为了解决突破函数作用域的限制才有了闭包这种概念

闭包的优点及缺点

优点

  • 闭包可以捕获外部作用域的变量,因此可以在事件处理程序和回调函数中使用闭包来访问外部作用域的变量,从而实现更灵活和可复用的代码
  • 私有化数据,在私有化数据的基础上保持数据

缺点

  • 可能会导致内存泄漏,内部的变量不会被自动回收掉

哪些里面应用的闭包

  1. 常用的回调函数
  2. 函数节流:是确保函数特定的时间内至多执行一次。
  3. 函数防抖:是确保函数特定的时间内不再被调用执行
for(let i = 0; i < 3; i++) {
   setTimeout(() => {
     console.log(i)
   }, 300)
}

这里涉及到很多概念:

  1. varlet 的区别之一: var声明会有变量提升,let 则没有,且let会有块级作用域 block scope
  2. 块级作用域到底是什么:通俗解释就是变量声明之后,能访问到该变量的区域(先忽略暂时性死区)。而能访问到的所有作用域(包括块级、函数级、全局)是因为词法作用域。
  3. 词法作用域到底是什么:是访问规则,即内部的作用域可以访问外部作用域的这样一个事实。比如你定义一个函数,该函数内部可以访问全局作用域中的变量或者函数。而如果你定义了一个函数,且这个函数与词法作用域的变量产生关联(访问该变量等)就会产生闭包。而闭包能让你在函数执行时仍然访问到该变量,可以理解成记住了该变量。(这就是为什么闭包可能产生内存泄露,因为变量一直被引用,无法被Garbage Collection
  4. 还有一个特别的点:for let会产生多个块级作用域,至于为什么,标准里的: 262.ecma-international.org/6.0/#sec-cr…

由上述四点可以得出:setTimeout的回调函数产生了闭包,回调函数一共执行三次,三次执行的函数所关联的是三个不同的块级作用域(闭包),而因为闭包记住的三个块级作用域中的i的值分别是0,1,2。 所以打印结果就是 0 1 2