美文网首页我爱编程
探索奥秘~闭包

探索奥秘~闭包

作者: 小鱼儿_逆流而上 | 来源:发表于2018-04-12 00:14 被阅读0次

    在我学习初期我对闭包的理解也不是很深,最近在学习了js更深入的知识后,对闭包有了更加深入的了解,下面我就跟大家详细解读一“闭包”!

    一、首先什么是闭包?

    “官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
    当一个函数能够记住并访问到其所在的词法作用域及作用域链,特别强调是在其定义的作用域外进行的访问,此时该函数和其上层执行上下文共同构成闭包。
    

    相信很少有人能直接看懂这句话,因为他描述的太学术。我想用如何在Javascript中创建一个闭包来告诉你什么是闭包,因为跳过闭包的创建过程直接理解闭包的定义是非常困难的。看下面这段代码:

    function a(){
     var i=0;
     function b(){
     alert(++i);
     }
     return b;
    }
    var c = a();
    c();
    

    这段代码有两个特点:
    1、函数b嵌套在函数a内部;
    2、函数a返回函数b。
    这样在执行完var c=a()后,变量c实际上是指向了函数b,再执行c()后就会弹出一个窗口显示i的值(第一次为1)。这段代码其实就创建了一个闭包,为什么?因为函数a外的变量c引用了函数a内的函数b,就是说:
    当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个闭包。

    **需要明确的几点:
    1、闭包一定是函数对象(wintercn大大的闭包考证)
    2、闭包和词法作用域,作用域链,垃圾回收机制息息相关
    3、当函数一定是在其定义的作用域外进行的访问时,才产生闭包
    4、闭包是由该函数和其上层执行上下文共同构成

    二、接下来我们来看下闭包是如何产生的?
    现在我假设JS引擎执行到这行代码
    let baz = foo();
    此时,JS的作用域气泡是这样的:


    38412730-97a3fd6e-39bc-11e8-9a53-208d71ca98eb-660x572.png

    这个时候foo函数已经执行完,JS的垃圾回收机制应该会自动将其标记为”离开环境”,等待回收机制下次执行,将其内存进行释放(标记清除)。
    但是,我们仔细看图中粉色的箭头,我们将bar的引用指向baz,正是这种引用赋值,阻止了垃圾回收机制将foo进行回收,从而导致bar的整条作用域链都被保存下来。
    接下来,baz()执行,bar进入执行栈,闭包(foo)形成,此时bar中依旧可以访问到其父作用域气泡中的变量a。

    我们借助chrome的调试工具看下闭包产生的过程:
    当JS引擎执行到这行代码let baz = foo();时:


    38487982-9301fd1a-3c14-11e8-8238-57321d84eb05-660x439.jpg

    图中所示,let baz = foo();已经执行完,即将执行baz();,此时Call Stack中只有全局上下文。
    接下来baz();执行:


    38488086-e0d4349a-3c14-11e8-84af-ea05e7546a43-660x439.jpg
    我们可以看到,此时bar进入Call Stack中,并且Closure(foo)形成。
    三、闭包的应用场景
    1、保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

    2、在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
    以上两点是闭包最基本的应用场景,很多经典案例都源于此!

    闭包的奥妙
    闭包,它并不是很神秘,反而是在我们的程序中随处可见,让我们静下心来,品味闭包的味道,走进“闭包”世界,感受它的魅力所在!

    相关文章

      网友评论

        本文标题:探索奥秘~闭包

        本文链接:https://www.haomeiwen.com/subject/qhetkftx.html