Skip to content

JS的闭包是什么?怎么用?

论述题

「是什么、怎么做、解决了什么问题、优点是、缺点是、怎么解决缺点」

是什么

闭包是JS的一种语法特性。

闭包 = 函数 + 自由变量

对于一个函数来说,变量分为:全局变量本地变量(局部变量)、自由变量

顶级作用域声明的变量是全局变量window 的属性是全局变量

局部变量:定义在函数内部,只能在函数中使用的变量,作用范围是从函数开始到结尾,即在 {} 里。

怎么做

javascript
let count = 0
function add () { // 访问了外部变量的函数
	count += 1
}

把上面代码放在「非全局环境」里,就是闭包。(为了区分全局变量的影响)

注意,闭包不是 count,闭包也不是 add,闭包是count + add 组成的整体

怎么制造一个「非全局环境」呢?答案是立即执行函数

javascript
const add2 = function () {
  var count = 0
  return function add () { // 访问里外部变量的函数
    count += 1
  }
}()

此时 add2 其实就是 add,我们可以调用 add2

javascript
add2()
// 相当于
add()
// 相当于
count += 1

至此,我们就实现了一个完整的「闭包的应用

注意:闭包 ≠ 闭包的应用,但面试官问「闭包」但时候,应该回答「闭包的应用

解决了什么问题

作用

  1. 避免污染全局环境。(因为用的是局部变量)
  2. 提供对局部变量的间接访问。(因为只能 count += 1 不能 count -= 1)
  3. 维持变量,使其不被垃圾回收。

优点:简单,好用。

缺点:闭包使用不当可能造成内存泄露。

注意,重点是「使用不当」,不是闭包。

「闭包造成内存泄漏」这句话以讹传讹很多年了,曾经旧版本IE点bug导致的问题。

举例说明

javascript
function test() {
  var x = { name: 'x' };
  var y = { name: 'y', content: '_______这里很长,有很多字符_________' }
  return function fn() {
    return x
  }
}

const myFn = test() // myFn 就是fn 了
const myX = myFn() // myX 就是 x 了
// 请问 y 会消失吗?

对于一个正常的浏览器来说,y会在一段时间后自动消失(被垃圾回收器给回收掉)。

但旧版本的IE并不是正常的浏览器,所以是IE的问题。

应该尽量少用闭包,因为有些浏览器对闭包的支持不够好,所以不能说「闭包造成内存泄露」

怎么解决缺点

慎用,少用,不用。

参考

「闭包的应用」的其他案例

javascript
var api = function () {
  let lives = 3
  return () => { lives -=1 }
}()

api()

「每日一题」JS 中的闭包是什么?