zoukankan      html  css  js  c++  java
  • 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

    <!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">
    
        <!--js中的闭包学习地址: https://mp.weixin.qq.com/s/puZeIzQ6XCVNIFjrqSz7Tg -->
        <!-- <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();
               console.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(){
                        console.log(i+' ');
                     },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>num0)
                {
                   console.log(x);
                }
            }
    
            void function(fn2){
              var num0 =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 -->
        <script>
          //声明普通函数
          function fn()
          {
              console.log("张培月");
          }
          
          //然后将函数的名字去掉即是匿名函数:
          /**匿名函数,咦,运行时,你会发现报错啦!
            function (){
                console.log("张培跃");
            }
    
            到此,你会发现单独运行一个匿名函数,
            由于不符合语法要求,报错啦!解决方法只需要给匿名函数包裹一个括号即可:
          */
          //匿名函数在其它应用场景括号可以省略
    
        (function (){
        //由于没有执行该匿名函数,所以不会执行匿名函数体内的语句。
        console.log("张培跃");
        })
    
        //如果需要执行匿名函数,在匿名函数后面加上一个括号即可立即执行!
        (function (){
            //此时会输出张培跃
            console.log("张培跃");
        })()
        
        //倘若需要传值,直接将参数写到括号内即可:
        (function (str){
        //此时会输出张培跃好帅!
        console.log("张培跃"+str);
        })("好帅!")
        
        /*
        匿名函数的应用场景:  
        1.事件
        */    
        var sub=document.querySelector("#sub");//获得按钮元素
        //给按钮增加点击事件。
        sub.onclick=function(){
            alert("当点击按钮时会执行到我哦!");
        }
        /*
        2.对象
        */
        var obj ={
            name:"张培跃",
            age:18,
            fn:function(){
              return "我叫" + this.name +"今年" + this.age +"岁了!";
            }
        };
        console.log(obj.fn());//我叫张培跃今年18岁了!
    
        /*
        *3.函数表达式
        */
        //将匿名函数赋值给变量fn
        var fn = function()
        {
            return "我是一只小小鸟,怎么飞也飞不高";
        }
        console.log(fn());//我是一只小小小小留下,怎么飞也飞不高!
    
        /*
        * 4.回调函数
        */
        setInterval(function(){
          console.log("我其实是一个回调函数,每次1秒钟会被执行一次");
        },1000);
    
        /*
        *5.返回值
        */
        //将匿名函数作为返回值
        function fn()
        {
            //返回匿名函数
            return function(){
                return "张培跃";
            }
        }
    
        //调用匿名函数
        console.log(fn()());//张培跃
        //或者如下调用
        var box = fn();
        console.log(box());//张培跃
    
        /*
        模仿块级作用域
        块级作用域,有的地方称为私有作用域。JavaScript中是没有块级作用域的,例如:
        */
        if(1==1) //条件成立,执行if代码块语句
        {
          var a =12;//a 为全局变量
        }
        console.log(a);//12
    
        for(var i=0;i<3;i++)
        {
           console.log(i);
        }
        console.log(i);//3
    
        /*
        * if(){} for(){} 等没有自己的作用域,
        如果有,出了自己的作用域,声明的变量就会立即被销毁了。
        但是咱们可以通过匿名函数来模拟块级作用域:
        */    
        
        (function(){
         //这里是我们的块级作用域(私有作用域)
        })();
    
    
        //尝试块级作用域:
        function fn(){
    
          (function(){
              var la="啦啦啦!";
          })();
    
          console.log(la);//报错---la is not defined
        }
        fn();
    
        /*
        *总结:匿名函数的作用:
        1.通过匿名函数可以实现闭包,闭包是可以访问在函数作用域内定义的变量的函数,
        若要创建一个闭包,往往需要用到匿名函数
        2.模拟块级作用域,减少全局变量。执行完匿名函数,存储在内存中相对应的变量会被销毁,
        从而节省内存。再者,在大型多人开发的项目中,使用块级作用域,
        会大大降低命名冲突的问题,从而避免产生灾难性的后果。
        自此开发者再也不必担心搞乱全局作用域了          
        */
        </script>
    </head>
    <body>
        <div>
            <label>JS:监听文本框内容变化</label>
            <input id="input">
            <!-- 匿名函数的应用场景:1.事件 -->
            <input type="button" value="点我啊!" id="sub">
        </div>
    </body>
    </html>
    
    “fool me once,shame on you. fool me twice, shame on me.”,翻译过来的意思是“愚弄我一次,是你坏;愚弄我两次,是我蠢”。
  • 相关阅读:
    软件测试入门知识
    QTP小应用一则
    频分时分波分码分
    解析UML9种图的作用
    OSI七层模型
    暑期实习心得
    0724工作小结 SQL查库是重点
    0723脚本存储过程的学习
    0722工作日志
    工作之余回味了曾经的写过的小说
  • 原文地址:https://www.cnblogs.com/newcapecjmc/p/13814179.html
Copyright © 2011-2022 走看看