最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 到底什么是闭包?

    正文概述 掘金(Up&up)   2021-03-14   691

    闭包(closure)在 JavaScript 中可以说是无处不在,但由于我们无法直接观测到它,所以就导致我们经常忽视了它的存在,以致于难以掌握。

    事实上,每当我们创建一个函数闭包也会随之产生,它是基于词法作用域书写代码时自然产生的结果。

    const a = 1
    
    function foo() {
      console.log(a)
    }
    

    上面这段代码有闭包吗?如果有,你能说说它具体是什么吗?

    接下来,让我们一起来探究一下到底什么是闭包。

    基本概念

    我们先来看看 MDN 文档是如何描述闭包这个概念的:

    MDN 把闭包的基本概念描述得已经非常清楚了,但是个人觉得还缺少一条关键信息,那就是在什么情况下闭包会产生作用。

    在《你不知道的 JavaScript(上卷)》一书中是这么定义闭包的:

    这句话的后半段非常重要,一来加强说明了闭包一定会在函数创建时产生,再者是指出了闭包在什么情况下发挥作用。

    这句话前半段中的词法作用域可以理解为函数所在位置的作用域,但实际上还包含了在该函数中能够通过作用域访问到的所有变量(引用)。

    综上所述,我们可以将闭包的基本概念总成为两点:

    1. 闭包会在函数被创建时,自动根据所在的词法作用域产生。

    2. 闭包主要是在函数不在所在词法作用域中执行的情况下起作用。

    若隐若现的闭包

    让我们先来看这段代码:

    function foo() {
      const a = 1
    
      function bar() {
        console.log(a)
      }
    
      bar() // 1
    }
    
    foo()
    

    调用 foo() 函数时,bar() 函数也被执行,基于词法作用域的查找规则,bar() 函数可以访问外部作用域(foo() 函数的作用域)中的变量 a

    这段代码中有闭包吗,从上文中闭包的基本概念来看,肯定产生了闭包,只是我们很难找到它们的踪迹。

    第一个闭包是 foo() 函数在创建时产生的,它引用了外部全局作用域,第二个闭包是 bar() 函数在创建时产生的,它引用了 foo() 函数的作用域以及外部全局作用域。

    再来看这段代码:

    function foo() {
      const a = 1
    
      function bar() {
        console.log(a)
      }
    
      return bar
    }
    
    const baz = foo()
    
    baz() // 1
    

    与上一段代码不同,在调用 foo() 函数时,我们将内部的 bar() 函数引用作为返回值赋值给了变量 baz,当我们调用 bar() 函数时,实际上调用的就是 bar() 函数本身。

    一般来说,当 foo() 函数执行完时,内部作用域会被销毁,垃圾回收机制会被触发,变量 a 占用的内存会被释放。但是当我们在全局作用域中调用 bar() 函数后,还是打印出了变量 a 的值,这就说明 foo() 函数的内部作用域没有被销毁,变量 a 还在内存中。

    这就是闭包的强大之处,bar() 函数产生的闭包阻止了 foo() 函数的内部作用域被销毁。这也佐证了闭包基本概念中的第二点,闭包主要是在函数不在所在词法作用域中执行的情况下起作用。

    我们再来看看其他情况:

    function sleep(fn, delay, ...data) {
      setTimeout(function bar() {
        fn(data)
      }, delay)
    }
    
    sleep((who) => {
      console.log( `${who} wake up`) 
    }, 1000, 'jack')
    
    // 约一秒后
    // jack wake up
    

    这段代码中,当 sleep() 函数执行完后,它的作用域也不会立即消失,这是因为 bar() 函数依然保持着有 sleep() 函数作用域的闭包。

    1000 毫秒后,bar() 函数顺利地从 sleep() 函数作用域中找到了变量 fn 和变量 data

    像类似 setTimeout() 这样的异步任务中,我们很容易发现闭包的影子,因为它们都使用了回调函数,该函数中又引用了外层作用域的变量。

    const foo = (function (param) {
      return function bar () {
        console.log(param)
      }
    })('A')
    
    foo() // A
    

    上面这段代码使用了 IIFE 立即执行函数,当该函数执行完时,它创建的作用域会一直保留,因此 bar() 函数能够访问到变量 param,当然这也是闭包的作用。

    原来如此,IIFE 也是容易发现闭包的地方,闭包真是无处不在。

    闭包的副作用?

    长久以来,闭包一直被一部分人认为是最容易造成内存泄露的罪魁祸首。

    const foo = (function () {
      const a = 1
      return function () {
        console.log(a)
      }
    })()
    

    这段代码中,只要变量 foo 指向的匿名函数存在,分配给变量 a 的内存就永远不会得到释放。

    的确,滥用闭包会有内存泄露的危险,但是更加值得我们关注的是它带来的好处。

    总之,我们应该去认识和拥抱它,并利用好这一特性带给我们的魔力,用过的人都说好!

    小结

    本文主要探究了 JavaScript 中闭包这一晦涩难懂的特性,总结了它的基本概念以及特点。

    以上就是本文的全部内容,希望对你有所帮助。

    本人水平有限,如有错误或有争议的地方,请及时指出。

    最后,感谢阅读,欢迎交流分享!


    起源地下载网 » 到底什么是闭包?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元