美文网首页前端
闭包、定时器

闭包、定时器

作者: 饥人谷_任磊 | 来源:发表于2016-09-15 20:35 被阅读56次

    问答

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

    • 闭包
      闭包就是能够读取其他函数内部变量的函数。在JavaScript中,只有函数内部的子函数才能够读取内部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数。”
      比如在函数内部读取它的内部变量:
    function f1(){
           var n = 999;
           function f2(){
                  console.log(n);
           }
           return f2;
    }
    var result = f1();
    resulet(); //999
    

    上段代码中,函数f2就是闭包。

    • 作用
      1.可以读取函数内部的变量。
      2.让这些变量始终保存在内存中。
      举例来看闭包的作用:
    function f1(){
           var n = 999;
           nAdd = function(){n+=1}
           function f2(){
           console.log(n);
           }
            return f2;
    }
    var result = f1();
    result();//999
    nAdd();
    result();//1000
    

    这段代码中,函数f2就是闭包,它一共运行了2次,第一次是999,第二次是1000,这说明了函数f1的局部变量n一直保存在内存中,并没有在f1调用后清除。

    原因是f2被赋给了一个全局变量result,导致f2一直存在内存中,而f2的存在依赖于f1,所以f1也始终在内存中。

    2. setTimeout 0有什么作用

    setTimeout函数用来指定某个函数或某段代码在多少毫秒之后执行,它接受两个参数,要执行的代码和以毫秒表示的时间。
    举例:

    setTimeout(function() {
           console.log("jirengu")
    },1000);//返回1,一秒钟后打印出jirengu
    

    返回的整数表示这个定时器的编号,以后可以用来取消这个定时器,但实际任务中,很少这么用。

    setTimeout函数的重点是第二个参数,第二个参数是一个表示等待多长时间的毫秒数,但是经过该时间的后执行的代码不一定会执行。

    在理解这句话之前先说说JavaScript的运行机制:
    JavaScript是一个单线程的解释器,一定时间之内只能执行一段代码。
    为了控制要执行的代码,就有一个JavaScript任务队列。这些任务会按照将它们添加到队列的顺序执行。
    setTimeout()的第二个参数告诉JavaScript再过多长时间把当前任务添加到队列中。也就是说setTimeout()指定的任务肯定是最后一个添加到队列中的,这个指定的任务要等到前面的任务执行完了以后再执行。
    所以setTimeout函数的第二个参数应该理解为等到前面的任务执行之后再经过指定的毫秒数后来执行当前的任务
    举例来说明吧:

    console.log(1)
    setTimeout(function() {
           console.log(2)
    },100);
    var t1 = Date.now()
    for(var i=0;i<10000;i++){
          console.log(3);
    }
    var t2 = Date.now()
    console.log(t2-t1);
    
    运行结果

    从运行结果中看到,console.log(2)确实是最后运行,而且是等到完成for循环的后再运行的,而不是之前理解的100ms之后会立即运行。

    那么最后解释setTimeout 0就比较容易了,指的是setTimeout指定的任务在现有的任务执行之后立即执行。

    代码题

    1.下面的代码输出多少?修改代码让fnArr[i]()输出i.使用两种以上的方法

        var fnArr = [];
        for (var i = 0; i < 10; i ++) {
            fnArr[i] =  function(){
                return i;
            };
        }
        console.log( fnArr[3]() );  //输出结果为10
    

    在for循环中,i的值并没有随着循环保存在函数中,所以关键在于如何保存i的值,所以有了以下闭包的方法:

    //方法一:
    var fnArr = [];
    for (var i =0; i<10; i++){
           fnArr[i] = (function(n){
                return function(){
                      return n;
                }
    })(i)
    }
    console.log(fnArr[3]());
    
    //方法二:
        var fnArr = [];
        for(var i=0; i<10; i++){
            (function(n){
                fnArr[n] = function(){
                    return n;
                }
            })(i)
        }
        console.log(fnArr[3]());
    

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

    var Car = //todo;
    Car.setSpeed(30);
    Car.getSpeed(); //30
    Car.accelerate();
    Car.getSpeed(); //40;
    Car.decelerate();
    Car.decelerate();
    Car.getSpeed(); //20
    Car.getStatus(); // 'running';
    Car.decelerate(); 
    Car.decelerate();
    Car.getStatus();  //'stop';
    //Car.speed;  //error
    

    实现代码:

        var Car = (function(){
            var speed = 0;
            function setSpeed(){
                speed = arguments[0];
            }
            function getSpeed(){
                console.log(speed);
                return speed;
            }
            function accelerate(){
                speed += 10;
            }
            function decelerate(){
                speed -= 10;
            }
            function getStatus(){
                if(speed > 0){
                    console.log("running");
                    return "running";
                }else {
                    console.log("stop");
                    return "stop";
                }
            }
            return {
                "setSpeed":setSpeed,
                "getSpeed":getSpeed,
                "accelerate":accelerate,
                "decelerate":decelerate,
                "getStatus":getStatus
            }
        })()
        Car.setSpeed(30);
        Car.getSpeed(); //30
        Car.accelerate();
        Car.getSpeed(); //40;
        Car.decelerate();
        Car.decelerate();
        Car.getSpeed(); //20
        Car.getStatus(); // 'running';
        Car.decelerate();
        Car.decelerate();
        Car.getStatus();  //'stop';
    
    运行结果

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

    //方法一
        function interval(func,time){
            var intv = function(){
                func.call(null);
                setTimeout(intv,time);
            }
            setTimeout(interval,time);
        }
    
        interval(function(){
            console.log(1);
        },1000);
    
    
    //方法二
        function interval(func, time){
            setTimeout(function(){
                func.call(null);
                interval(func,time);
            },time)
        }
    
        interval(function(){
            console.log(1);
        },1000)
    

    需要注意的是,使用setTimeout并不能完全模拟出setInterval的功能。
    在问答1中介绍过,setTimeout是等前面的任务执行之后再开始计算时间间隔。
    而setInterval函数指定的是“开始执行”之间的间隔,并不考虑每次任务执行本身所消耗的时间,比如,setInterval指定每100ms执行一次,每次执行需要5ms,那么第一次执行结束后95毫秒,第二次执行就会开始。如果某次执行耗时特别长,比如需要105毫秒,那么它结束后,下一次执行就会立即开始。

    4.写一个函数,计算setTimeout平均最小时间粒度。

        function getMini() {
            var i = 0;
            var start = Date.now();
            var clock = setTimeout(function(){
                i++;
                if(i === 1000){
                    clearTimeout(clocl);
                    var stop = Date.now();
                    console.log((stop-start/1000));
                }
                else{
                    var clock = setTimeout(arguments.callee,0);
                }
            },0)
        }
        getMini();
    

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

    var a = 1;
    setTimeout(function(){
        a = 2;
        console.log(a);
    }, 0);
    var a ;
    console.log(a);
    a = 3;
    console.log(a);
    

    由于setTimeout函数会把指定的任务放在最后执行,所以以上代码实际执行顺序如下:

    var a = 1;
    var a ;
    console.log(a);//1
    a = 3;
    console.log(a);//3
    setTimeout(function(){
        a = 2;
        console.log(a);//2
    }, 0);
    

    所以最后的输出结果为1,3,2.

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

    var flag = true;
    setTimeout(function(){
        flag = false;
    },0)
    while(flag){}
    console.log(flag);
    

    while方法会无限循环,无任何输出。
    同上题,setTimeout函数会把指定的任务放在最后执行,所以实际执行顺序如下:

    var flag = true;
    while(flag){}
    console.log(flag);
    setTimeout(function(){
        flag = false;
    },0)
    

    变量flag一直为true,所以while方法会无限循环。

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

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

    实现代码:

    //方法一
        for(var i=0; i<5; i++){
            (function(n){
                setTimeout(function(){
                    console.log("delayer:" + n);
                },0)
            })(i);
            console.log(i);
        }
    //方法二
         for(var i=0; i<5; i++){
             setTimeout((function(n){
                 return function(){
                     return console.log("delayer:" + n);
                 }
             })(i),0)
             console.log(i);
         }
    
    运行结果

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

    相关文章

      网友评论

        本文标题:闭包、定时器

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