JavaScript之闭包

来源:互联网 发布:象棋软件开局库 编辑:程序博客网 时间:2024/05/17 09:21

一、闭包是什么?

闭包是可以访问独立变量的函数。因为函数是可以创造作用域的,函数的内部作用域是可以访问外部作用域,但是外部作用域是不能访问内部作用域的,所以在函数内声明的变量可以通过声明的内函数进行访问,然后把这个内函数返回,就可以在函数外面直接访问函数内的独立变量。

作用域链:就是函数可以创造作用域,在函数内部也可以声明函数,这就形成了作用域套作用域的链式结构,简称为作用域链。js分为内部作用域和外部作用域,访问变量都是从内向外沿着作用域链进行查找的。

二、闭包需要解决的问题是什么?

在函数外部访问函数内部声明的变量。

三、闭包的基本模式:

//1.基本模式:        function outer(){            var data=10;            function inner(){                return data;            }            return inner;        }        var fn=outer();        fn();//2.获取独立变量和设置独立变量:        function outer(){            var data=10;            function getData(){                return data;            }            function setData(value){                data=value;                return data;            }            return {                getData:getData,                setData:setData            };        }        var fn=outer().setData;        fn(100);   //100        fn=outer().getData;        fn();      //10//3.优化代码        function outer(){            var data=10;            function inner(value){                if(value){                    data=value;                }                return data;            }            return inner;        }        var fn=outer();        fn();

总结起来就是:返回一个函数,用这个函数获得数据;返回一个对象,这个对象包含函数,来操作这个数据


四、循环点击事件存在的问题及解决方案:

代码:

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Title</title>    <style>        div{            border:solid 1px #000;            margin-bottom: 10px;            width:70px;            height:20px;        }    </style>    <script>        window.onload=function(){            var divs=document.getElementsByTagName("div");            for(var i=0;i<divs.length;i++){                var div=divs[i];                div.onclick=function(){                    console.log("我是第"+i+"个div");                }            }        }    </script></head><body>    <div>第0个div</div>    <div>第1个div</div>    <div>第2个div</div>    <div>第3个div</div>    <div>第4个div</div>    <div>第5个div</div>    <div>第6个div</div></body></html>

期待的效果是:点击“第0个div”,显示“我是第0个div”,点击“第1个div”,显示“我是第1个div”。但是实际显示的是点击任何一个,显示的都是,“我是第7个div”。



//原因:因为在触发div点击onclick事件的时候,for循环已经执行完毕,这时候返回的i就是7,所以点击每一个div都是返回7

解决办法:使用闭包:将i变量保护起来

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Title</title>    <style>        div{            border:solid 1px #000;            margin-bottom: 10px;            width:70px;            height:20px;        }    </style>    <script>        window.onload=function(){            var divs=document.getElementsByTagName("div");            for(var i=0;i<divs.length;i++){                var div=divs[i];                function outer(){                    var j=i;                    return function(){                        console.log("我是第"+j+"个div");                    }                }                div.onclick=outer();   //利用闭包解决了循环点击出现的问题            }        }    </script></head><body>    <div>第0个div</div>    <div>第1个div</div>    <div>第2个div</div>    <div>第3个div</div>    <div>第4个div</div>    <div>第5个div</div>    <div>第6个div</div></body></html>



五、for循环中的setTimout出现的问题:

<script>        for(var i=0;i<10;i++){            setTimeout(function(){                console.log(i);            },0);        }        var a=10;        alert(a);</script>

setTimout(function(){},time)是至少在time时间后执行回调函数。在js中有分主要任务和次要任务,主要任务就是主逻辑;次要任务就是setTimout和setInterval等回调函数。所以上述代码会先for循环,和setTimeout,执行下面的alert 10,再执行回调函数console.log(i),所以最终返回的也是10次10.

解决办法:利用闭包保护i变量:

<script>        for(var i=0;i<10;i++){            function outer(){                var j=i;                function inner(){                    console.log(j);                }                return inner;            }            setTimeout(outer(),0);        }</script>



六、斐波拉契数列的性能问题:

<script>        //1. 用递归的方法实现斐波拉契数列:        //增加count计数来计算每次fib函数执行的次数:        //存在的问题:重复计算的次数太多,        var count=0;        function fib(n){            count++;            if(n<=2){                return 1;            }else{                return fib(n-1)+fib(n-2);            }        }        fib(5);        console.log(count);   //9        count=0;        fib(20);        console.log(count);   //13529        //2. 优化时间复杂度,可以使用非递归的方式实现斐波拉契数列:        var count1=0;        function fib1(n){            var a, b,res;            a=b=1;            if(n<=2){                return 1;            }else{                for(var i=3;i<=n;i++){                    count1++;                    res=a+b;                    a=b;                    b=res;                }                return res;            }        }        fib1(20);   //6765        console.log(count1);   //18</script>

用递归实现的斐波拉契数列,就存在大量的重复计算,这种重复计算会导致count是成倍成指数型的增加,耗费计算机内存。

除了使用我上面代码中提到的用非递归的方式进行实现,还有一种方法也可以:就是将每次计算出来的fib(n)值存到数组里,这样如果有就直接取,没有就计算,计算好了就存;这样也是大大减少了重复计算量。

//3.利用数组存储的方式优化递归实现的斐波拉契数列算法:        var arr=[];        var count2=0;        function fib2(n){            count2++;            var num=arr[n];            if(num){                return num;            }else{                if(n<=2){                    return 1;                }else{                    num=fib2(n-1)+fib2(n-2);                    arr[n]=num;                    return num;                }            }        }        fib2(20);    //5        console.log(count2);

除此之外,这个arr变量暴露在全局,很可能被别人随意修改,我们可以通过闭包的方式保护这个变量:

//4.利用闭包优化递归实现的斐波拉契数列算法:(保护了数组变量arr)        function fib3(){            var arr=[];            function fibIn(n){                var num=arr[n];                if(num){                    return num;                }else{                    if(n<=2){                        return 1;                    }else{                        num=fib2(n-1)+fib2(n-2);                        arr[n]=num;                        return num;                    }                }            }            return fibIn;        }        var fn=fib3();        fn(5);


七、Github源码地址:

https://github.com/spicyboiledfish/JavaScript-testJS


原创粉丝点击