JS 闭包

68 阅读2分钟

一 什么是闭包

闭包是由捆绑起来(封闭的)的函数和函数周围状态(词法环境)的引用组合而成。

可以把闭包看作是在函数环境中,一个函数(必须引用外部数据)与其周围状态(词法环境)的组合。

也就是说,闭包包含两个部分,一是执行环境A,二是在A中的函数B,闭包通过B的调用而被激活使用。

举个例子:在makeFunc函数中,displayName函数形成了闭包,return出的displayName函数,实际是一个闭包,它包含了整个执行环境,所以能在后面的调用中访问到执行环境中的name属性。

function makeFunc() {
  const name = "Mozilla";
  function displayName() {
    console.log(name);
  }
  return displayName;
}
const myFunc = makeFunc();
myFunc();

二 闭包在js中的独特性

闭包(Closure)是许多编程语言中常见的特性,不同语言对闭包的支持和实现方式有所不同。

相比于其它编程语言,js中的闭包更加灵活,它可以捕获并保留其词法作用域中的变量,并允许修改。

这种灵活性也导致了js的闭包可能会有内存泄漏的风险。

三 关于闭包的内存泄露问题

所谓内存泄漏,即程序动态分配内存后,未通过相应方法进行释放,泄露的内存无法被程序或操作系统回收,导致可用内存持续减少

js在使用闭包时,有两种情况会导致内存的泄露。

  1. 持有本该被销毁的函数,造成其关联的词法环境无法被销毁,比如事件监听结束后移除事件监听,但绑定的事件函数还存在于内存之中。

  2. 当有多个函数共享词法环境时,可能导致词法环境膨胀,从而发生无法访问但无法销毁的数据,比如由于c、d共享词法环境,在调用a后,无法访问函数c,但也无法销毁它。

function a(){
    let b = 0;
    function c(){
        console.log(b);
    }
    function d(){
        console.log(b++);
    }
    return d;
}
let d = a();

总结:闭包不是在javascript中特有的,但于其它语言中的闭包相比,javascript的闭包更灵活,且有内存泄露的风险。