• 实例详解js闭包(一)闭包语法形式推导及其基本作用


      在学习前端的过程中,不可避免的要学习到js闭包这个知识点,很多朋友感到对闭包很难理解,也不清楚它有什么用。本文就详细介绍一下闭包,并通过几个小例子来说明下闭包的用处。 

    一、闭包的概念

          闭包的英文单词是Closure,我先给闭包可以这样下个简单的定义,这个定义不是官方的,是我自己理解的。 

      定义:如果在函数A的内部,声明了另外一个函数B,并且函数B可以访问A中定义的变量或是数据,此时函数A和函数B就形成了闭包。

          闭包其实讲述了函数与函数的关系。

    二、闭包的基本形式

          我们直接来个例子加以说明:

          例1:闭包基本形式

                

          这里定义了一个函数f1,在f1的内部又定义了一个局部变量num和函数f2,并且f2调用了局部变量num,这个代码结构已经形成了闭包。

          不过,这样的代码看看也罢,貌似是没有任何作用的。我们再改改。

          例2.闭包基本形式2

          

          查看运行结果,我们看到输出了10 。 我们下一个简单的结论:闭包可以让一个局部的变量,在它的作用域之外访问到。您可能不同意我这个结论。不过没关系,请继续往下看。

    三、闭包的模式

          闭包其实有2种模式:

          1.函数形式的闭包

          2.对象形式的闭包

          咱们刚才在上边的例子都是函数形式的闭包。我们举一个对象模式的闭包,作为了解。

          例3.对象模式的闭包

          

          这里函数f1和它内部的对象obj形成了闭包,原因是:1.obj是在f1内部声明的,2.obj的age属性访问了f1内部声明的局部变量num。

          好了,最常见的闭包,还是函数模式的,所以这个了解就好,我们下面的例子都是以函数模式来讲解的。

    四、闭包的作用

          闭包有什么用呢?它的作用在于两点:1.延长局部变量的作用域链。2.缓存数据。其实第2点就是通过第1点来达到的效果。我们举个例子。

          例4.闭包作用演示1

         

      我们前面的例子仅仅是在f1的内部声明了一个f2,而这里,我们不仅在f1内部声明了一个函数,并且把这个内部的函数作为外部函数f1的返回值return了回来。

      这下就有趣了。当我们执行语句:var fun = f1();的时候,变量fun里存储的是什么?当然是f1函数的返回值,只不过这时候的返回值恰好又是一个函数对象。其实在这里,就fun相当于是一个函数表达式了。这条语句的调用结果,等价于下面这种写法

      例5.简单函数表达式语法

       

      这就是一个简单的函数表达式,既然如此,fun当然可以通过fun()的形式来调用这个函数。不过这都不是重点。

      重点是,我们在全局作用域下,通过fun这个引用,访问到了f1里定义的局部变量num。

      你如果觉得不是,你再想想,我是不是能这样:

      例6.闭包作用演示2

       

      修改过后,是不是已经说明了问题,什么问题?我们确实在全局作用域下,拿到了一个函数内部声明的局部变量的值。或者换句话说,我们在函数外部访问到了内部声明的局部变量。

      看到这里,你可能还是觉得有点迷糊,我是访问到了,但是这和在内部函数中直接console.log()输出这个局部变量num的值有什么区别呢?你说的访问,也仅仅是读而已,并不能代表能操作它,比如改变它的值,所以你并不认为例6是真正的在外部访问了内部的数据。您是不是也有如此的疑惑呢?

      下面再看一组例子,为您解惑。

          例7.非闭包的数据访问操作

      

          请问,此例中代码执行结果是什么?答案是输出3次11 。 这个原因很简单,3次完全独立的函数调用而已,所以每次调用的时候都会开辟一个全新的内存空间来存储f1中声明的局部变量num,并且赋初值为10,那么++过后必然都是11 。所以这个输出结果毫无悬念。

          再看这个例子的变形,也就是例8

          例8.闭包版的数据访问操作

         

          先说输出结果吧,3次调用,输出的是     

      

          11,12,13  这个结果奇怪吗?不奇怪吗?    这就是闭包的作用。

          在代码的第27行,我们声明了一个变量fun,给他复制为f1()函数执行的返回值,也就是内部声明的那个函数对象。接下来3次调用都是使用的同一个对象,而这个fun指向的函数对象内部访问了定义它的外部函数f1声明的一个局部变量num。所以,3次调用fun()时,操作的num++,都是针对内存里的同一个变量进行的++,所以我们看到的结果就是11,12,13 。下面通过一个图来说明下例8

       

          说明下图上表示的意思。

          1.黑色的大矩形框,表示代码执行时的js环境

          2.红色矩形框代表js引擎线程在执行代码是的内存空间。当然这个代码在执行时是有先后的,内存也会有先后的变化,不过我为了简单起见,把所有的过程都画一张图里了。

          3.小的黑色矩形框表示在预解析时,已经加载到内存中的f1函数的代码

          那么运行结果是这样的,首先执行var fun = f1(); 这时候会把小黑方框中的代码加载到主线程去执行,执行的结果就是得到那2个蓝色的矩形框。

          小一点的是那个num变量,大一点的蓝色矩形框代表那个返回的函数对象。从这张图我们能清晰的看到,整个代码在执行时,只有1个函数对象fun,也只有一个变量num。由于num被fun对象所引用,所以,虽然超出了它的作用域,它也无法释放掉。这也证明了,我们说的,闭包延长了变量的作用域链。     

      现在我们可以得出结论了,闭包的作用就是:

      1.延长变量的作用域链。

      2.缓存数据

  • 相关阅读:
    Array之foreach
    gulp之gulp-uglify模块的大坑-------------默认不支持IE8
    Only the original thread that created a view hierarchy can touch its views
    android 组件隐藏
    android 字体加粗
    android studio 创建图标
    Can't create handler inside thread that has not called Looper.prepare()
    Failed to connect to /127.0.0.1:8080
    socket failed: EACCES
    android 无法import
  • 原文地址:https://www.cnblogs.com/ldq678/p/9810900.html
Copyright © 2020-2023  润新知