美文网首页
闭包、定时器

闭包、定时器

作者: 饥人谷区子铭 | 来源:发表于2016-10-05 23:51 被阅读0次

    1.什么是闭包? 有什么作用

    • 定义:闭包就是嵌套在函数里面的内部函数,并且该内部函数可以访问外部函数中声明的所有局部变量,参数和其他内部的函数。当该内部函数在外部函数外被调用了,就生成了闭包。
    (1)闭包包括了函数和创建该函数的环境。这个环境由闭包创建时在作用域中的任何局部变量组成。
    (2)[函数]和[函数内部能访问到的变量(也叫环境)]的总和,就是一个闭包。
    (3)闭包不是非得return回一个函数,return出一个对象也可以是闭包。
    (4)特点一:作为一个函数变量的一个引用,当函数返回时,其处于激活的状态。
    (5)特点二:一个闭包就是当一个函数返回的时候,一个没有释放资源的栈区。
    
    • 作用:
      (1)保存变量现场,封装私有变量
      (2)隔离作用域;
      (3)做计时器
      (4)独立了一个命名空间
      (5)闭包会使变量驻留内存,不会被自动清除;使用时应谨慎,以免引起内存泄露,影响性能
    闭包的缺点:
    1.在IE9之前因为使用不同的垃圾收集机制会导致循环引用会造成内存泄漏
    2. 闭包会携带包含函数的作用域,所以会比其他函数占用更多内存,过度使用闭包会造成内存占用过多
    
    例子:
    在函数内部读取它的内部变量:
    function fn(){
        var a = 1;
        function add(){
          a+=1
          console.log(a)
        }
        return add //add就是一个桥梁
    }
    var r = fn() //闭包,包括函数add和包含变量a的环境
    r() //输出2  
    //add被赋给了一个全局变量r,导致add一直存在内存中,而add的存在依赖于a,所以a也始终在内存中。
    因此:闭包就是一个桥梁,连接着外部环境和父函数的局部变量
    
    经典例子:
    (function(){
       var arr = []
       for(var i=0;i<5;i++){
            arr[i] = function(){
              console.log(i)
            }
      }
      arr[1]()
    })()
    为什么!!!无论arr[1]-arr[5]()输出结果都是5,因为所访问的i是外部函数的,
    当循环完以后,i已经累加到5了,所以无论多少输出当然是5(作用域链)
    
    利用闭包特点可以保存变量现场(让变量不被释放):
    (function() { 
        var arr = [] 
        for (var i = 0; i < 5; i++) { 
              arr[i] = function (n) { 
                return function () { 
                          alert(n) 
                } 
              }(i) //把i传递进来,作用域中就可以取到当前的i
         } 
         arr[1]()//输出当前值1
    })()
    

    2.setTimeout 0 有什么作用

    • 定义:setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。

    (1)setTimeout(func|code, delay),一般接受两个参数,第一个参数可以是字符串,也可以是func函数名,第二个参数是设置多少毫秒ms后执行代码.如果省略第二个参数,该参数默认为0.
    (2)setTimeout(“code”,millisec):执行代码必须用字符串形式,millise设置在多少毫秒之后执行这段代码。
    eg:setTimeout('console.log(2)',1000); //1000ms后控制台输出2
    (3)setTimeout(fn,millisec)中fn为函数名,也可以是回调函数,但记得是写函数名而不是写调用函数,millise设置在多少毫秒之后执行这个函数。
    (4)可以把setTimeout中的函数排在任务队列末尾,等待其他语句全部执行完毕再开始立即执行,可以理解为setTimeout优先级在任务队列中最低

    setTimeout (‘console.log(最尾)',0)//代表当前任务队列执行后才立刻执行输出“最尾”
    setTimeout('console.log(最尾了但还要1000ms后再执行)',1000)//代表当前任务队列执行后再过1000ms才执行
    例子:
    setTimeout('console.log(1)',1000); //右边为执行顺序:  2
    console.log(2); //                                  3
    console.log(3); //                                  4
    setTimeout('console.log(4)',0);//                   1
    

    总结:setTimeout 0 有什么作用?(任务队列中优先级最低)
    可以让setTimeout中的函数排在任务队列末尾,等待其他语句全部执行完毕再开始立即执行


    3.下面的代码输出多少?修改代码让fnArr1输出 i。使用两种以上的方法

    题目:
    var fnArr = []; 
    for (var i = 0; i < 10; i ++) { 
            fnArr[i] = function(){ 
                return i; 
          }; 
    } 
    console.log( fnArr[3]() );~~~无奈都是输出10,因为执行函数之前 i=10;在for循环中,
    i的值并没有随着循环保存在函数中,所以关键在于如何保存i的值,所以有了以下闭包的方法:
    ——————————————————————————————————————————————————————
    //使用闭包函数实现返回当前传入的值
    方法一:
    var fnArr = []; 
    for(var i = 0;i < 10;i++) { 
         fnArr[i] = function(n){//用数组记录下循环中每次的值
             return function(){
               return n;
             }
         }(i) //每次循环传入实参i
    } 
    console.log( fnArr[3]() );//输出3
    ————————————————————————————————————————————————————
    方法二:
    function func(val) { 
        return function() { 
              return val;
           }; 
      } 
    var fnArr = []; 
    for (var i = 0; i < 10; i++) {
     //将返回值(i)存入fnArr数组 
          fnArr[i] = func(i); 
    } 
    console.log(fnArr[3]());//输出3
    ——————————————————————————————————————————————————————
    方法三:
    var fnArr = []; 
    for(var i=0;i<10;i++){
      (function(n){
        fnArr[n] = function(){
          return n
        }
      })(i)//将i作为实参传入立即执行函数
    }
    console.log( fnArr[3]());//输出3
    

    4.使用闭包封装一个汽车对象,可以通过如下方式获取汽车状态

    解答:
    var Car = (function(){
        var val;
        function setSpeed(val){
          speed = val;
        }
        function getSpeed(){
          return speed;
        }
        function accelerate(){
          speed +=10;
        }
        function decelerate(){
          speed -=10;
        }
        function getStatus(){
          if(speed===0){
            return "stop";
          }else{
            return "running";
          }
        }
        return {
          "setSpeed":setSpeed,
          "getSpeed":getSpeed,
          "accelerate":accelerate,
          "decelerate":decelerate,
          "getStatus":getStatus
        };
    }());
    
    // var Car = //todo;
    console.log(Car.setSpeed(30));
    console.log(Car.getSpeed()); //30
    console.log(Car.accelerate());
    console.log(Car.getSpeed()); //40;
    console.log(Car.decelerate());
    console.log(Car.decelerate());
    console.log(Car.getSpeed()); //20
    console.log(Car.getStatus()); // 'running';
    console.log(Car.decelerate()); 
    console.log(Car.decelerate());
    console.log(Car.getStatus()); //'stop';
    //Car.speed; //error
    

    5.写一个函数使用setTimeout模拟setInterval的功能

    方法一:
    var i = 0;
    function fn(){
        setTimeout(function(){
            console.log(i);
            i++
            fn()  //递归不断调用自己
        },2000);
    }
    fn()
    方法二:
    var i = 0;
    function fn(){
          var clock = setTimeout(function(){
          console.log(i);
          i++
          clock = setTimeout(arguments.callee,2000); //递归不断调用自己
        },2000);
    }
    fn()
    每隔两秒输出:0,1,2,3,4,5,6~~~~~~~~~~
    需要注意的是,使用setTimeout并不能完全模拟出setInterval的功能。
    在问答1中介绍过,setTimeout是等前面的任务执行之后再开始计算时间间隔。
    而setInterval函数指定的是“开始执行”之间的间隔,并不考虑每次任务执行本身所消耗的时间,比如,
    setInterval指定每100ms执行一次,每次执行需要5ms,那么第一次执行结束后95毫秒,第二次执行就会
    开始。如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。
    

    6.写一个函数,计算setTimeout平均[备注:新加]最小时间粒度(参考)

    function getMini() { 
        var t1 = Date.now(); //开始执行函数的时间 
        var i = 0; 
        var clock = setTimeout(function(){ 
              i++; 
          if(i === 1000) { //计算1000次 
              clearTimeout(clock); //停止 
              var t2 = Date.now(); //结束时间 
              console.log((t2-t1)/i); //平均值得到最小粒度 
          }else{
    
          //arguments 当前函数的参数"数组" 
          //arguments.callee 调用这个"数组"的函数,也就是当前函数 
          //相当于循环执行当前函数, 直到 i 满足判断语句中的值 
              clock = setTimeout(arguments.callee,0);
          } 
      },0) //此处一定是0,无间隔地循环执行直到到1000为止
    }
    getMini()
    输出大约4.249秒~4.5秒
    

    7.下面这段代码输出结果是? 为什么?

    题目:
    var a = 1;
    setTimeout(function(){
         a = 2; 
         console.log(a);
    }, 0);
    var a ;
    console.log(a);
    a = 3;
    console.log(a);
    
    结果: 2.png

    setTimeout的delay值被设为0,也就意味着里面的函数要等待其他语句全部执行完毕才会运行,setTimeout( ,0),代码会在最后执行。所以最先输出的是1,然后赋值3给a,最后执行setTimeout()里面的代码


    8.下面这段代码输出结果是? 为什么?

    题目:
    var flag = true;
    setTimeout(function(){ 
        flag = false;
    },0)
    while(flag){}
    console.log(flag);
    ------------------
    等同于
    var flag = true;
    while(flag){}  //一直卡在这位置不往下执行了
    console.log(flag)
    setTimeout(function(){
        flag = false
    })
    

    分析:只要while(expression){statement},ex条件一直为true,那么就不断循环执行st,setTimeout的delay值被设为0,也就意味着里面的函数要等待其他语句全部执行完毕才开始再运行。而while(flag){}因为flag为true的关系永远不会停止,所以console.log(flag)也就永远不会执行。


    9.下面这段代码输出?如何输出delayer: 0, delayer:1...(使用闭包来实现)

    题目:
    for(var i=0;i<5;i++){ 
        setTimeout(function(){ 
          console.log('delayer:' + i ); 
        }, 0); 
       console.log(i);
    }
    
    结果3.png

    解答:用闭包保存循环中每次的i值,闭包只是起到保存变量的作用,并未因此改变setTimeout( ,0)中的代码依然放置事件队列最尾处才执行的规则

    方法一:
    for(var i=0;i<5;i++){ 
      (function(n){
         setTimeout(function(){      
           console.log('delayer:' + n ); 
        }, 0);
      })(i)
       console.log(i);
    }
    

    结果3.png

    方法二:
    for(var i=0;i<5;i++){ 
        setTimeout((function(n){ 
          return function(){
             console.log('delayer:' + n ); 
          }
        })(i), 0); 
       console.log(i);
    }
    

    结果3.png

    方法三:
    for(var i=0;i<5;i++){
      fn = function(n){
        return function(){
          console.log("delayer:"+n)
        }
      }(i)
      setTimeout(fn,0)
      console.log(i)
    }
    
    结果3.png

    本文版权归本人和饥人谷所有,转载请注明来源。

    相关文章

      网友评论

          本文标题:闭包、定时器

          本文链接:https://www.haomeiwen.com/subject/sgwsyttx.html