最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端学习之面试题系列:(二)对JS闭包的理解

    正文概述 掘金(Crystal_Wei)   2021-03-01   769

    写在前面

    闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。因此,闭包成为前端面试中经常被问到的一个知识点,比如面试官可能会问:“你了解什么是闭包吗?闭包的作用是什么?请谈谈你对闭包的理解”等等。本文是笔者在学习闭包的过程中总结的笔记,如有理解错误的地方,还请各位大佬多多指正~~~

    闭包的理解

    1、什么是闭包?

    • 概念

      百度百科原话是这么说的:闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成定义在一个函数内部的函数。在本质上,闭包就是将函数内部和函数外部连接起来的桥梁。

    • 概念的拆分理解

      1. 闭包是什么?

        从概念中的“闭包就是能够读取其他函数内部变量的函数”这句话中抽取主谓宾可知,闭包是函数

      2. 闭包在哪里?

        从概念中的“闭包可以理解成定义在一个函数内部的函数”这句话可知,闭包是定义在函数内部的

      3. 闭包能干啥?

        从概念中的“闭包就是能够读取其他函数内部变量的函数”这句话可知,闭包能读取其他函数内部变量

    • 在代码中理解

      语言总是那么苍白无力,那么就让我们通过代码来感受感受到底什么是闭包?

      function addCounter(){    //addCounter()是一个函数,(外层函数)
          var count = 0         //count是一个被addCounter创建的内部变量(局部变量)
          function add(){       //add()是定义在函数addCounter()内部的函数(闭包)
              return count += 1
          }
          return add            
      }
      var closure = addCounter()  //定义一个全局变量closure来接收函数addCounter的返回值
      console.log(closure)        //closure的值就是add函数
      console.log(closure())      //closure()就是执行add函数,使count值增1,即count=1
      console.log(closure())      //注意这里的执行结果是2了
      

      执行结果:

      ƒ add(){ 
                return count += 1
              }
      1
      2
      

      从上面的代码可知,闭包(add函数)的存在,允许定义在addCounter函数外部的closure在可以读取到函数内部定义的局部变量count,从而实现count的增1运算。

    • 闭包的作用

      • 能够访问局部变量

        概念中有这么一句话 “闭包就是能够读取其他函数内部变量的函数”,这里的其他函数就是指闭包的外层函数,外层函数内部定义的变量就是局部变量。

        在上述代码中,add函数(闭包)能够访问它的外层函数addCounter内部定义的局部变量count

      • 保护这些局部变量

        保护这些局部变量是指,闭包可以保护这些局部变量在函数执行完后不被销毁,一直保存在内存中

        在上述代码中,局部变量count 就是这样受到闭包的保护,在函数addCounter执行完毕后,没有被销毁,一直保存在内存中,所以后面可以通过closure修改值。

    2、闭包解决经典问题

    面试时非常大的概率会遇到下面这道面试题,问你输出什么?

    for(var i = 1; i <= 5; i++){
        setTimeout(function(){
            console.log(i)
        }, 1000)
    }
    
    • 输出结果分析

      第一次见到这种题时,很自然的觉得会每隔1s连续打印1 2 3 4 5。

      然而事与愿违,这个程序的执行结果却是1s后同时打印出5个6

      要想知道这个代码的输出结果,那么就需要了解这个程序的执行顺序。我们之所以会认为每隔1s连续打印1 2 3 4 5,是因为我们默认每循环一次,console.log(i)就会执行一次。事实上,setTimeout是一个异步函数,它的第一个参数是一个回调函数,这个程序的正确执行顺序是,先执行完循环体,(可以理解为此时已经复制好了5份setTimeout函数),此时的 i已经是6了;然后在1s后开始回调setTimeout中的函数function(){console.log(i)},因此会在1s后同时打印5个6。

    • 代码的修改

      当你回答这个程序的执行结果是1s后同时打印5个6后,面试官会问你如何修改代码,可以输出1 2 3 4 5?

      修改代码的方式有很多种,这里主要解释以下两种解决方案:

      • 方案1:直接将var改为let
        for(let i = 1; i <= 5; i++){
            setTimeout(function(){
                console.log(i)
            }, 1000)
        }
        

        为什么改为let就可以了呢?原因是 let有块级作用域,而var没有

        在这段程序中,如果改为let关键字,那么在每次循环时,let的值就会和循环体绑定在一起,这样在回调时,就不会输出同样的值了。

        关于letvar的区别,可以看笔者的往期文章 前端学习之面试题系列:(一)var、let、const的区别

        关于let的深入理解,可以看这篇文章 我用了两个月时间才理解let

      • 方案2:利用闭包
        for(var i = 1; i <= 5; i++){
            (function(j){
                setTimeout(function timer(){
                    console.log(j)
                },1000)
            })(i)
        }
        

        我们首先利用立即执行函数将i的值传给j,这样每次循环的i值都会被保存在函数内部,当执行闭包函数timer时,就可以访问外部函数中保存好的j值,而不是循环结束后i的值了。

      • 其余方案:
        • 利用setTimeout函数的第三个参数保存i

          for(let i = 1; i <= 5; i++){
              setTimeout(function() {
                  console.log(i)
              },1000,i)
          }
          
        • setTimeout函数的第一个参数改为立即执行函数

          for(var i = 1; i <= 5; i++){  
              setTimeout(console.log(i),1000)   //第二个延迟时间参数不起作用
          }
          
          for(let i = 1; i <= 5; i++){
              setTimeout((function() {
                  console.log(i)
              })(),1000)                        //第二个延迟时间参数不起作用
          }
          
    • 参考文章

      关于JS闭包

      闭包

      彻底理解setTimeOut


    起源地下载网 » 前端学习之面试题系列:(二)对JS闭包的理解

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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