美文网首页JavaScript
闭包、定时器

闭包、定时器

作者: Iswine | 来源:发表于2016-09-07 01:20 被阅读97次

    1、何为闭包?有什么用?

    闭包专业的解释:
    闭包是有权访问另一个函数作用域的变量的函数。 简单的说,Javascript允许使用内部函数—即函数定义和函数表达式位于另一个函数的函数体内。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。

    但我相信初学者看到上面的解释绝对是一脸懵逼,闭包应该是大家在JavaScript的学习中所遇到的第一个理解起来相对复杂的概念。我们先用一个经典的BUG来引出为什么要使用闭包:

    function power() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push(function () {
                return i * i;
            });
        }
        return arr;
    }
    
    var results = power();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];
    

    按照我们正常的逻辑,所希望得到的结果应为:

    f1();   //1
    f2();   //4
    f3();   //9
    

    但实际上却是:

    f1();   //16
    f2();   //16
    f3();   //16
    

    为什么?
    原因在于将result[0],result[1],result[2]分别绑定给f1,f2,f3时,绑定的仅仅是power()函数,并未立即计算。当获取结果时(即f1()),开始执行函数,此时的i已经变成了4,故所有的结果均为16。
    怎么办?
    从上述分析可知,根源在于函数绑定时没有即时保存结果,所以我们添加立即执行函数and闭包,让每一次变量都单独赋值,且最终结果不会发生改变:

    function power() {
        var arr = [];
        for (var i = 1; i <= 3; i++) {
            arr.push((function (n) {
                return function () {
                    return n * n;
                }
            })(i));
        }
        return arr;
    }
    
    var results = power();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];
    

    这时,所得到的结果就是我们先前所期望的值。我们着重分析一下闭包的结构与特性:

    • 函数中套用函数;
    • 父函数的返回值为子函数;
    • 子函数能调用父函数以及更加外层的变量;
    • 外部亦能随意给子函数传递参数以实现功能;
    • 当父函数返回子函数时,子函数所接收的变量与参数均被保存在其中。

    有什么用?
    由特性可以看出来,闭包可以调用并保存外部参数、变量,并且能够保证返回函数中变量的独立性。这就可以给我们封装功能,实现模块化提供了巨大的便利。由于JS没有私有函数这一个概念,我们可以用这种办法来模拟出私有函数,具体实现过程可以看第4题。

    2、setTimeout 0 有什么作用

    • setTimeout(function(){},ms)是延迟函数,其内部有两个参数,第一个参数为所需要实现的功能函数,第二个参数为延迟的毫秒数。
    • setInterval(function(){},ms)是间隔函数,参数设置与setTimeout一样,区别在于setTimeout只会在延迟XX毫秒后执行一次函数,而setInterval会在延迟XX毫秒后执行函数后继续在延迟XX毫秒执行函数,无限循环。

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

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

    代码1:

    var fnArr = [];
    for (var i = 0; i < 10; i ++) {
        fnArr[i] =  (function (n){
            return function () {
                return n;
            }
        }(i));
    }
    console.log( fnArr[3]() );  
    

    代码2:

    var fnArr = [];
    for (var i = 0; i < 10; i ++) {
        fnArr[i] =  (function (){
            var n=i;
            return function () {
                return n;
            }
        }());
    }
    console.log( fnArr[3]() );  
    

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

    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 speeed = 0;
        function setSpeed(n) {
            speed = n;
            return speed;
        }
        function accelerate() {
            speed += 10;
            return speed;
        }
        function decelerate() {
            speed -= 20;
            return speed;
        }
        function getSpeed() {
            return speed;
        }
        function getStatus() {
            if (speed > 0){
                return "running";
            }
            if (speed == 0){
               return "stop";
            }
            if (speed < 0){
                return "error!";
            }
        }
        return {
            accelerate: accelerate,
            decelerate: decelerate,
            getSpeed: getSpeed,
            getStatus: getStatus,
            setSpeed: setSpeed
        };
    }());
    

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

    var i = 0;
    function intv(){
        clock = setTimeout(function(){
            console.log(i++);
            intv();
        },1000);
    }
    function stop(){
        clearTimeout(clock);
    }
    intv();
    

    6、写一个函数,计算setTimeout最小时间粒度

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

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

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

    依次输出 1,3,2.setTimeout函数的优先级最低,会放在程序最后执行,故实际上代码应该是:

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

    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;
    },0)
    

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

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

    setTimeout优先级最低,故先出0,1,2,3,4五个console.log(i)的值,循环完毕i=5,故输出5个delayer: 5;
    闭包实现需求:

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

    相关文章

      网友评论

        本文标题:闭包、定时器

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