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

    正文概述 掘金(白唯)   2021-03-15   647

    本篇文章假定你对 JS 已经有一定的基础,如果对执行环境以及作用域链有一定的了解或者怕冗长的概念者,请直接跳到第三步实践环节。

    1.写在前面

    JavaScript 的领域,关于对闭包的解读实在太多了,各大社区随便一搜就是一划不见底的海量技术贴,但是其中充斥了大量的没自己独到见解的随手 copy 的概念堆砌文,虽然它们其中也不乏四两拨千斤让人豁然开朗的深度技术好贴,但是都缺乏让人实际的图文场景并且能够长期使用记住的效果,因此本篇文章是我在 <<js高程设计>>闭包一节以及修言的《从编译原理的角度理解作用域》作为理论基础,然后通过实践写出来的一篇文章,如有错误指出,还望指正。

    2.尽量凝练的前置知识

    说到闭包,我们又不得不说到JS 的作用域链,说到作用域链,又不得不说到JS的执行环境,因此说闭包,其实是对几个重要的JS核心概念做解释和梳理,但是为了避免在用代码解释闭包之前做大量的术语解释,我只简要列以下几点前置只是铺垫,如有自己不熟悉的地方,自行补全。

    别走,我发誓只有这一个概念要看。

    • 执行环境(也叫执行上下文)

    JavaScript代码执行的时候会进入不同的执行环境,执行环境定义了变量或者函数能够访问的其他数据,这些执行环境会形成一个执行环境栈,下面就是它的组成:

    执行环境
    variable object(变量对象)函数声明,局部变量,函数参数,他们的集合就叫变量对象[[scope]]属性指向作用域链,作用域链是一个由变量对象组成的带头结点的单向链表,其主要作用就是进行变量查找,而 scopde 属性是指向该链表的头结点,稍后我们将在实践中看到这一个属性。this 指针指向一个环境对象,而不是一个执行环境

    以上就是执行环境的组成。

    作用域链和我们的执行调用栈是两个东西,在外部函数执行栈出栈后,因为内部函数仍然有引用,因此作用域链不会删除对应的变量对象,我们仍然可以通过作用域链来访问外部环境中的变量对象,这就是闭包现象。只有完全解除引用,作用域链才去维护这个状态,删除其变量对象。

    以上两句话非常重要,现在你可能还不能完全消化它,那么下面我们就进入实战环节吧。

    3.通过 debugger图解JS 的闭包

    一句废话也不多说,先直接上代码:

     function first(first) {
          const firstName = first
          const dept = 1
          debugger  // 第一个 debugger
          return function second(second) {
            const secondName = firstName + '/' + second
            dept = 2
            debugger // 第二个 debugger
            return function third(third) {
              const thirdName = secondName + '/' + third
              dept = 3
              debugger  //  第三个 debugger
            }
          }
      }
    const x = first('白')('小')('唯')
    const y = first('白')('小')
    debugger // 第四个 debugger
    

    first函数内部return 回来一个second 函数,second 函数里面又return 回来一个名为 thrid 函数,并且我们分别在合适的时机debugger 了一下,下面让我们在浏览器中跑一下,看看会出现什么情况。

    • 第一次 debugger

    通过 debugger图解JS 的闭包

    在first 函数入栈时,我们发现局部变量有 dept=1,first:'白’,firstName:'白',局部变量以及函数参数都在,没有任何特殊的事情发生,OK,让我们进入下一步。

    • 第二次 debugger

    通过 debugger图解JS 的闭包 second 函数进入了调用栈,如果你对作用域这一块内容熟悉的话,你应该不会惊讶,我们scope作用域中多了一个名为 first 的闭包,里面存储的正是 first 函数里面的变量对象(如果忘了这个名词,可以回头查看),因此通过这里我们可以知道,内部的 second 函数是可以访问到 first 函数里的属性的,让我们接着看第三个 debugger 会发生什么。

    • 第三次 debugger

    通过 debugger图解JS 的闭包

    third 函数进入了调用栈,并且跟第二次 debugger 如出一辙,作用域不仅将外部 second 函数的变量对象包含了起来,还包括了first 函数的变量对象,因此她可以访问并且修改 dept,并且成功置为 3。

    学过作用域的我们都知道,函数内部访问一个变量时,在自身作用域没找到,就会从函数外部去找,直至找到全局作用域,那么问题来了,如果外部函数已经弹出执行栈,仍然可以访问吗?

    这就是第四个 debugger 的作用所在了,为了检测 third函数可以在 first 和 second 已经出栈的情况下还能访问自身不存在的变量的情况,在代码中我们通过 y = first('白')('小')来获得第三个函数,让我们看看会发生什么。

    • 第四次 debugger

    通过 debugger图解JS 的闭包

    我们可以看到成功获取了 third 函数,并且有了一个惊人的发现,在 firstsecond 的执行环境被弹出的情况下,我们的 third 函数的 [[scopes]]属性仍然包含着他们的变量对象,回过头看,这不就是我们的作用域链吗?!

    根据查阅资料显示:这里 y 的[[scopes]]里面存放的是运行期上下文的集合,因为我们并没有清除对 third 的引用,因此可以知道,我们的作用域链上仍然有着 firstsecond 函数的变量对象,这样就自然就能在 firstsecond 函数弹出调用栈之后还能访问到,因为函数弹出调用调用栈,只是销毁了内部的指向作用域链的指针 scope,而我们作用域链却没有因此去删除相应的变量对象,因为我们 third 函数仍然在引用。下面,让我们再次来读刚开始那段话,是否又有了新的感受呢?

    作用域链和我们的执行调用栈是两个东西,在外部函数执行栈出栈后,因为内部函数仍然有引用,因此作用域链不会删除对应的变量对象,我们仍然可以通过作用域链来访问外部环境中的变量对象,这就是闭包现象。只有完全解除引用,作用域链才去维护这个状态,删除其变量对象。

    这篇文章差不多就到这里了,你对闭包有了新的认识了吗?


    起源地下载网 » 通过 debugger图解JS 的闭包

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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