乱谈闭包

424 阅读2分钟

关于闭包,有各种各样的解释,百科给出的解释是:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

我的理解是:函数执行形成私有的作用域。 那么他就有两个作用:保护和保存。

闭包的保护作用

闭包形成的私有作用域可以保护里面的私有变量不受外界干扰。例如我们经常使用的:

var a = 5;
(function () {
    var a = 10;
    console.log(a)   //  ---->10
})()
console.log(a)   //  ---->5

这时在全局中有个a变量,私有作用域中也有个a变量,使用闭包,我们再修改私有作用域中的同名变量是,不会对全局产生影响,避免造成全局污染。

闭包的保存作用

形成一个不销毁的私有作用域,保护函数执行的上下文。例如之前我们写函数的防抖和节流的时候:

function debounce(fn, time) {
    let Timer;
    return () => {
        Timer && clearTimeout(Timer)
        Timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, time)
    }
}

debounce函数保存了一个计时器id的变量Timer,debounce执行 形成一个可以让返回函数访问到的私有作用域(函数的上级作用域是它定义时所处的作用域),每次调用返回函数的时候都可以访问得到这个Timer。

闭包的副作用

举个老是被拿来举的闭包例子:

var liList = document.querySelectorAll('li')
for (var i = 0; i < liList.length; i++) {
    (function (i) {
        liList[i].onclick = function () {
            console.log(i)
        }
    })(i)
}

由于返回的函数被外部的onclick事件引用了,闭包形成的私有作用域无法被销毁,内存无法被回收,回收机制参考js垃圾回收机制,容易造成内存泄漏。

若要手动释放内存,则那么写:

for (var i = 0; i < liList.length; i++) {
    liList[i].onclick = null
}

并不是所有闭包都会造成内存无法回收,例如:

function fn(i) {
    return function (n){
        console.log(i + n)
    }
}

fn(2)(3)

fn形成的私有作用于中,变量并没有被外界所引用,函数执行完成后,内存立刻进行回收。