闭包

183 阅读2分钟

1. 首先,闭包是什么?

闭包(closure):内部函数 总是可以访问其所在的 外部函数 中声明的 参数和变量,即使在其 外部函数被返回(寿命终结)了 之后。

2. 闭包的特点是什么

  • 让外部访问函数内部边浪成为可能
  • 局部变量会常住在内存中
  • 可以避免使用全局变量,防止全局变量污染
  • 会造成内存泄漏(有一块内存被长期占用,而不被释放)

3. 闭包的创建

闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。闭包会发生内存泄漏,每次外部函数执行的时 候,外部函数的引用地址不同,都会重新创建一个新的地址。 但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。

闭包内存泄漏为: key = value,key 被删除了 value 常驻内存中; 局部变量闭包升级版(中间引用的变量) => 自由变量;

4. 例子

那就通俗来点讲,闭包的定义其实也很简单:函数A内部有一个函数B函数B可以访问到函数A中的变量,那么函数B就是闭包

function A() {
    let a = 1
    window.B = function () {
        console.log(a)
    }
}
A()
B() // 1

说到底闭包存在的意义就是让我们可以简洁的访问函数内部的变量

经典面试题,循环中使用闭包解决 var 定义函数的问题

for (var i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i)
    }, i * 1000) 
}

运行结果: image.png

首先因为 setTimeout 是个异步函数,所以会先把循环全部执行完毕,这时候 i 就是 6 了,所以会输出一堆 6

经典面试题,也是经常见到的问题

解决方式有三种

1. 第一种就是闭包的方式
for (var i = 1; i <= 5; i++) {
    ;(function(j) {
        setTimeout(function timer() {
            console.log(j)
        }, j * 1000)
    })(i)
}

在上述代码中,我们首先使用了立即执行函数将 i 传入函数内部,这个时候值就被固定在了参数 j 上面不会改变,当下次执行 timer 这个闭包的时候,就可以使用外部函数的变量 j ,从而达到目的

2. 第二种就是使用 setTimeout 的第三个参数,这个参数就会被当成 timer 函数的参数传入
for (var i = 1; i <= 5; i++) {
    setTimeout(
        function timer(j) {
            console.log(j)
        },
        i * 1000,
        i
    )
}
3. 第三种就是使用 let 定义 i 来解决问题,这个也是最为推荐的解决办法
for (let i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i)
    }, i * 1000)
}