Skip to content
^finished

1. 闭包作用

  • 避免变量全局污染

  • 使数据私有化,外部无法修改内部的数据

  • 可以让外部使用内部的私有数据

以上这些作用,函数也可以实现

开始之前,你需要了解函数的基本使用以及作用域链。

2. 闭包的核心作用

  • 使变量可以驻留在内存,不被回收(普通函数不具备) 用代码来解释,写一个让变量 a 自增的函数。
js
let a = 10 // 全局变量,容易被污染
function fn() {
     a++
     console.log(a)
}
fn() // 11
fn() // 12
fn() // 13
let a = 10 // 全局变量,容易被污染
function fn() {
     a++
     console.log(a)
}
fn() // 11
fn() // 12
fn() // 13

几个人同时开发,对变量 a 污染,或者你自己忘记了又对变量 a 赋值了。

js
let a = 10 // 全局变量,容易被污染
··· 
a = 100 // 其他开发成员又对a进行赋值了(污染)
···
function fn() {
    a++
    console.log(a)
}
fn() // 101
fn() // 102
fn() // 103 被污染了
let a = 10 // 全局变量,容易被污染
··· 
a = 100 // 其他开发成员又对a进行赋值了(污染)
···
function fn() {
    a++
    console.log(a)
}
fn() // 101
fn() // 102
fn() // 103 被污染了

把这个变量写在函数的里面,那么 a 就不会被污染了。但是每次自增都重新从10开始。

js
function fn() {
    let a = 10 // 局部私有变量 
    a++ 
    console.log(a)
}
fn() // 11
fn() // 11
fn() // 11
function fn() {
    let a = 10 // 局部私有变量 
    a++ 
    console.log(a)
}
fn() // 11
fn() // 11
fn() // 11

怎么样让变量 a 长期驻留在内存?这时候就要用到闭包了。

闭包

只要在之前的函数里面再嵌套一个函数,把自增的代码放进去。仅需注意两点:

  1. 函数里面嵌套一个函数
  2. 里面的函数要使用到外层函数的变量,如果没有使用到,那么是不会形成闭包的。

我们需要执行变量 a 自增的操作,将内部函数 return 出去即可。

js
function fn() {
    let a = 10 // 局部私有变量 
    function () {
        a++ 
        console.log(a)
    }
}
function fn() {
    let a = 10 // 局部私有变量 
    function () {
        a++ 
        console.log(a)
    }
}

以上现在只是将闭包的形式写出,现在要把闭包创建出来,执行外部函数 fn() 即可。

js
function fn() {
    let a = 10
    return function () {
        a++ 
        console.log(a)
    }
}
fn() // 创建一个闭包,仅需执行一次fn()
function fn() {
    let a = 10
    return function () {
        a++ 
        console.log(a)
    }
}
fn() // 创建一个闭包,仅需执行一次fn()

用变量 ffn() 的执行存起来。

js
function fn() {
    let a = 10
    return function () {
        a++ 
        console.log(a)
    }
}
let f = fn()  // 创建闭包,可以通过断点在控制台看到
// 执行3次f
f() // 11 
f() // 12
f() // 13
function fn() {
    let a = 10
    return function () {
        a++ 
        console.log(a)
    }
}
let f = fn()  // 创建闭包,可以通过断点在控制台看到
// 执行3次f
f() // 11 
f() // 12
f() // 13

后面每次都是执行 f() 这个函数(内部函数)而不是 fn() ,外部 fn() 这个函数只是一开始创建闭包的时候执行了一次 ,a = 10 这句只会执行一次。

内存泄露

闭包里面的变量是不会被释放的,所以每次自增都会驻留下来。

双刃剑

闭包可以让变量驻留在内存,不被回收。

处理不当可能造成内存泄露,可以手动清除。

js
function fn() {
    let a = 10
    return function () {
        a++ 
        console.log(a)
    }
}
let f = fn()
f()
f()
f()
f = null //手动清除
function fn() {
    let a = 10
    return function () {
        a++ 
        console.log(a)
    }
}
let f = fn()
f()
f()
f()
f = null //手动清除

也并不是所有的闭包都需要清除,有一些内存泄露的情况是可以允许的。