JS中的闭包

转自:https://mp.weixin.qq.com/s/puZeIzQ6XCVNIFjrqSz7Tg
收藏:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

参考这位园友的文章:https://www.cnblogs.com/itjeff/p/10106855.html

<!-- https://mp.weixin.qq.com/s/puZeIzQ6XCVNIFjrqSz7Tg -->

<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JS中的闭包应用</title>
    <label>JS:监听文本框内容变化</label>
    <input id="input" type="text">
    <!-- <script>

        /*
        *一.闭包的概念和特性
        */
        function makeFab()
        {
           let last=1,current=1;
           return function inner()
           {
              [current,last] = [current + last,current]
              return last;
           }            
        }

        /*
        *这是一个生成斐波那契数列的例子。makeFab的返回值就是一个闭包,makeFab像一个工厂函数,
        每次调用都会创建一个闭包函数,如例子中的fab。
        fab每次调用不需要传参数,都会返回不同的值,因为在闭包生成的时候,它记住了变量last和current,以至于在后续的调用中能够返回不同的值。
        *能记住函数本身所在作用域的变量,这就是闭包和普通函数的区别
        *
        */
        let fab = makeFab();
        console.log(fab());//1
        console.log(fab());//2
        console.log(fab());//3
        console.log(fab());//5



       /*
        *二.闭包——函数式编程之魂
        */
        function confirm(confirmText,confirmCallback,cancelCallback){
               // 插入提示框DOM,包含提示语句、确认按钮、取消按钮
               // 添加确认按钮点击事件,事件函数中做dom清理工作并调用confirmCallback
               // 添加取消按钮点击事件,事件函数中做dom清理工作并调用cancelCallback
        }

        function removeItem (id) {
        confirm(‘确认删除吗?‘, () => {
            // 用户点击确认, 发送远程ajax请求
            api.removeItem(id).then(xxx)
        }, () => {
            // 用户点击取消,
            console.log(‘取消删除‘)
        })
        }

        /*
        *这个例子中,confirmCallback正是利用了闭包,创建了一个引用了上下文中id变量的函数,
        这样的例子在回调函数中比比皆是,并且大多数时候引用的变量是很多个。 
        试想,如果语言不支持闭包,那这些变量要怎么办?作为参数全部传递给confirm函数,
        然后在调用confirmCallback/cancelCallback时再作为参数传递给它们?显然,这里闭包提供了极大便利。
        *
        */



        /*
        *三.闭包的一些列子
        */

        //1.防抖,节流函数
        /*
        前端很常见的一个需求是远程搜索,根据用户输入框的内容自动发送ajax请求,然后从后端把搜索结果请求回来。
        为了简化用户的操作,有时候我们并不会专门放置一个按钮来点击触发搜索事件,而是直接监听内容的变化来搜索(比如像vue的官网搜索栏)。
        这时候为了避免请求过于频繁,我们可能就会用到“防抖”的技巧,即当用户停止输入一段时间(比如500ms)后才执行发送请求。
        */
        function debounce(func,time){
            let timer=0;
            return function(...args){
                timer && clearTimeout(timer)
                timer = setTimeout(()=>{
                   timer =0;
                   func.apply(this,args)
                },time)
            }
        }
        input.onkeypress = debounce(function(){
            console.log(input.value);//事件处理逻辑
        },500)

       //debounce函数每次调用时,都会创建一个新的闭包函数,该函数保留了对事件逻辑处理函数func以及防抖时间间隔time以及定时器标志timer的引用。

       /**
       *2.节流函数
       **/
       function throttle(func,time)
       {
           let timer =0;
           return function(...args){
               if(timer) return;
               func.apply(this,args)
               timer = setTimeout(() =>timer =0,time)
           }
       }        
    </script> -->

    <!-- js中闭包  学习地址:https://www.cnblogs.com/itjeff/p/10106855.html -->
    <script type="text/javascript">
            /*
            闭包的本质:在一个函数内部创建另一个函数
            3个特性:
            (1)函数嵌套函数。
            (2)函数内部可以引用函数外部的参数和变量。
            (3)参数和变量不会被垃圾回收机制回收。
            以下以闭包的两种主要形式来研究:
            */  
            
           //第1种形式:函数作为返回值
           function a()
           {
              var name =‘dov‘;
              return function()
              {
                  return name;
              }
           }
           var b = a();
           consolel.log(b());// 输出:dov
           /*
           解析: 在上面的代码中,a()中的返回值是一个匿名函数,这个函数在
           a()作用域内部,所以它可以获取a()作用域下变量name的值,将这个值作为返回值赋给
           全局作用域下的变量b,实现了在全局变量下获取到局部变量中的变量的值
           */

           //再来看一个闭包经典的例子
           function fn()
           {
               var num =3;
               return function(){
                   var n=0;
                   console.log(++n);
                   console.log(++num);
               }
           }
           var fn1 = fn();
           fn1();//   1  4
           fn1();//   1  5

           /*
           解析:一般情况下,在函数fn执行完后,就应该连同它里面的变量一同被销毁,
           但在这个例子中,匿名函数作为fn的返回值被赋值给了fn1,这时候就相当于fn1
           =function(){var n=0...},并且匿名函数内部引用着fn里的变量num,所以变量num
           无法被销毁,而变量n是每次被调用时新创建的,所以每次fn1执行完后它就把属于自己的变量
           连同自己一起销毁,于是最后就剩下了孤零零的num,于是这里就产生了内存消耗的问题。           
           */
          for(var i=0;i<5;++i)
          {
             setTimeout(function(){
                console.log(i+ ‘ ‘);
             },100);
          }

          /**
          解析:按照预期它依次输出 1 2 3 4 5 而结果它输出了五次5,这是为什么?
          原来由于js是单线程的,所以在执行for循环的时候定时器setTimeout被安排
          到任务队列中排队等待执行,而在等待过程中for循环就已经在执行,等到
          setTimeout可以执行的时候,for循环已经结束,i的值也已经编程5,所以打印
          出来五个5,那么为了实现预期结果应该如下改:(ps:如果把for循环里面的var变成let
          ,也能实现预期结果)
          */
          for(var i=0;i<5;++i)
          {
             (function(i){
                 setTimeout(function(){

                 },100);
             }(i));
          }
         /*
         引入闭包来保存变量i,将setTimeout放入立即执行函数中,将for循环中的循环值i作为参数
         传递,100ms后同时打印出  1 2 3 4 5
         那如果想实现每隔100ms分别输出数字,该如下改:
         **
         for(var i=1;i<5;i++)
         {
            (function(i){
                setTimeout(function(){
                   console.log(i);
                },i*100);
            })(i)
         }
        /*
          在这段代码中,相当于同时启动3个定时器,i*100是为4个定时器分别设置了不同的时间,
          同时启动,但是执行时间不同,每个定时器间隔都是100毫秒,
          实现了每隔100毫秒就执行一次打印的效果。
        **/

        //第2种形式:闭包作为参数传递
        var num0 =15;
        var fn1 =function(x){
            if(x>num)
            {
               console.log(x);
            }
        }

        void function(fn2){
          var num =100;
          fn2(30)
        }(fn1)

        /*
        在这段代码中,函数fn1作为参数传入立即执行函数中,在执行到fn2(30)的时候,
        30作为参数传入fn1中,这时候if(x>num)中的num取的并不是立即执行函数中的num,
        而是创建函数的作用域中的num,这里函数创建的作用域是全局作用域下,所以num取的
        是全局作用域中的值 15,即30>15,打印30
        */


        /*
        总结:闭包的好处与坏处:
        好处:
          (1)保护函数内的变量安全,实现封装,防止变量流入其他环境发生命名冲突;
          (2)在内存中维持一个变量,可以做缓存(但使用多了同时也是一项缺点,消耗内存)
          (3)匿名自执行函数可以减少内存消耗;

        坏处:
        (1)其中一点上面已经体现,就是被引用的私有变量不能被销毁,增大了内存消耗,造成内存泄漏,
        解决的方法是可以在使用完变量后手动将它赋值为null;
        (2)其次由于闭包涉及跨域访问,所以会导致性能损失,我们可以通过把跨作用域变量
        存储在局部变量中,然后直接访问局部变量,来减轻对执行速度的影响。
        **/
    </script>


    <!-- js中匿名闭包:阮一峰总结  学习地址:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html -->
    
    
    <!-- js中匿名函数  学习地址:https://www.cnblogs.com/ranyonsue/p/10181035.html -->
    
</head>
<body>
    <div>
        <label>JS:监听文本框内容变化</label>
        <input id="input">
    </div>
</body>
</html>

JS中的闭包

上一篇:jsp大文件(视频)上传教程


下一篇:html-列表标记