拥抱JavaScript闭包

来源:互联网 发布:mac子弹头专柜价格 编辑:程序博客网 时间:2024/05/28 18:44

        还记得初学 js 闭包的时候,总觉得他很高深莫测。每次当我觉得我要使用的时候,我都会悄无声息的使用其他的替代方法,只因为我觉得我会好像不会啊!我这样用要是出问题了我必回修改怎么办!不知道大家是不是也有这个感觉,我是这样子的。后来,我慢慢的开始工作的时候,别人总是会提起这个可怕的玩意,搞得我都不怎么想跟他们玩耍,但是时间长了,我觉得我应该花点时间去重新学习一下闭包,不然我发现跟那群 “ 闭包狂魔 ” 们没法玩耍了,于是我就有了如下的个人对闭包的理解。

        我是通过《你不知道的JavaScript》的帮助来理解闭包的,还记得作者写到:我马上要传授给你闭包的秘诀 ---> JavaScript中的闭包无处不在,你需要的能够识别并且拥抱他。我看到这句话以后才明白了为什么我身边的然都那么喜欢谈论闭包,因为他无处不在。那么接下来,我们一步一步的去解开为神秘他是无处不在的

       首先,我们先来看看什么叫做闭包

       我们应该清楚,闭包是产生在函数中的,所以函数调用时:当函数可以记住并访问所在的作用域时,就产生了闭包,即使函数是在当前作用域之外执行。是不是看了这段定义以后感觉都不知道我在干什么,其实我开始看到的时候就是这个感觉,如果这个看不懂,看下面这个闭包的经典案列:

function foo(){    var a = 2    function baz(){         console.log(a)    }    return baz;}var bar = foo()bar() //2 <---这里就是闭包的效果

        这里这个闭包相信大部分人能看出来,下面我们以他为例,来解析一下上面给出的定义。我们一句一句的看,当函数可以记住并访问所在的作用域时,这里说的是记住,并且访问所在的作用域时,上面的例子中,foo 将 baz 作为返回值赋给了bar,执行 bar()等价于执行 baz(),这时,baz是在 foo 外部执行的(满足了即使函数在当前作用域之外执行),但他依然能拿到 a = 2 ,再看定义,记住,baz是不是记住了他的外层 foo 作用域,因为a = 2 是在 foo 的作用域中定义的,所以,baz 满足了记住并且访问所在的作用域,所以baz就叫做闭包。这样子说其实还是不太明确的说法,没关系,我们可以一步一步的解开更深层次的东西。

       在这里,我们简单的考虑一下 js 的垃圾回收机制是怎么回收 foo 的作用域的。当我们定义了 foo,在他执行完毕以后,他的作用域(这里指的是他的内部作用域)就会被释放,也就是被回收了。当你在执行了 foo()时,他的作用域就应该被销毁了,但是由于你有执行了bar()(也就是foo 内部的 baz),他要访问 foo 的作用域,这时,就会阻止 foo 的作用域被垃圾回收机制回收,因为 baz 会告诉内存。

        由于内部函数 baz 有对 foo 作用域的引用而使得 foo 的作用域没有被回收,这里就是闭包最核心的部分。也就是说,内部函数 baz 保留了对外部函数 foo 作用域的引用的,这个保留的引用就是闭包。

        所以这里你应该清楚了,闭包是内部函数保留了对外部函数作用域的引用,所以嘛,深层次的说,这个保留的引用就是闭包。

        好了,foo 函数被我们巴拉的时间够长了,但是我不准备放过他,我们继续来巴拉它。

       上面讲的例子是不是觉得不是平时你常用的例子,没关系,下面来个你常用的:

      

function foo(message){    function bar(){        console.log(message)    }    setTimeout(bar,1000)}foo('Hello 闭包')

       现在,我们继续巴拉 foo ,直到我把巴拉 foo 理解闭包这个技能传授给你,如果你已经理解了上面的 “定制化”闭包,那么下面这个你就会很快搞定,废话不多说,我们开搞。

      首先当然是定义了一个 foo 函数,接着是经典的 bar ,这次下面是一个计时器函数,在 foo 函数执行 一秒以后,就会执行 bar 函数,打印我们穿进去的参数 message,这里需要说明一下,这个参数也属于 foo 作用域的变量,只是是隐式声明的。这里正常情况下,foo 在执行完以后,作用域就会被销毁,但是由于内部函数 bar 要在一秒后引用 foo 的作用域,他就被保留了下来。在一秒后进行了引用,这个引用就是闭包。是不是这样子有点贴近你的代码了。如果还没有,再看一个例子:

       相信作为一个前端你对JQuery一定不陌生,那看完下面这个例子,你就觉得贴近你的代码了:

     

function setupBot(name, selector) {$( selector ).click( function activator() {console.log( "Activating: " + name );} );}setupBot( "Closure Bot 1", "#bot_1" );setupBot( "Closure Bot 2", "#bot_2" );


        这里也是闭包的,你应该自己能分析闭包在哪了;


       下面我们来一次试探性的总结,看看你是不是真的理解了闭包 ---> 内部函数对外部函数作用域的引用。

        其实这个总结并不难,难的是你理解的过程,如果你做好准备了,看这句话:将函数当做当做第一级的值类型并到处传递,你就会看到闭包在这些函数里的应用。是不是看完这句话你又从理解变为懵逼了?其实我当时跟你的感觉是跟你一样的,因为这句话说得相当专业。但是我们可以将其翻译成简单点的话,比如下面这样子:将函数当做参数传入另一个函数里或者将函数当做返回值给传出去,如果你这样子干了,你就在使用闭包。有没有被自己的理解力给震惊到,如果你还是云里雾里的感觉,可以看下面的例子:

var b = 2;function aa(){    console.log(b)}setTimeout(aa,1000)


       看上面这段代码,是不是闭包呢?你不要急着回答问题,先跟我们之前的定义进行对照。首先 aa 是函数,我们把他当做值传给了定时器,你是不是像起了什么,对,这个是上面闭包的一种情况,然后就是记住并且访问,他记住了吗?其实计时器一秒后就会打印出 2 ,也就是全局变量 b 的值,也就是说 aa 记住了这个存在在全局作用域的 b 的值,并且在需要的时候进行了调用。这里你是不是开始想通了,是不是就是说 aa 取得了全局作用域的引用,我觉得这是废话,因为谁都能引用这个作用域。其实严格意义上来说他并不是闭包,因为全局作用域又不会被回收,因为按照闭包的特性,只要是在全局定义的函数,都会去的这个作用域的应用,并且会像上面定时器一样调用它们,那么这不是就符合了闭包的一切性质,所以和你想的一样,这也算是闭包,你可以这样子理解。 


 


0 0
原创粉丝点击