zoukankan      html  css  js  c++  java
  • JavaScript闭包详解

    闭包是JavaScript一个精髓和难点所在,很久以前就开始了解它,总想写点什么,遗憾的能力有限,包括您现在看到的这篇文章,大多数也是来自其他优秀的书籍和少部分自己的理解,我将只充当一个搬运工的角色,好了不多说了,开始正题吧!

    在了解闭包之前我们必须要先去深入了解JavaScript的作用域和作用域链。在JavaScript中作用域是在函数定义的时候决定的,而不是函数调用时决定的,举例说明。

           var count=5;
                function test1(){
                    var count =0;
                     function getCount(){
                        return count;
                     }
                     return getCount();
                } 
    console.log(test1());//0

    在上面的代码中我们最后得到的结果应该是0,原因很简单因为在执行getCount这个函数时,返回的会是局部变量count(这个可以去看一下JavaScript变量的查询规则)。那么再看下面这个例子。

                var count=5;
                function test1(){
                    var count =0;
                     function getCount(){
                        return count;
                    }
                    
                    return getCount;    
                }
                console.log(test1()());//0
    
    

    在这段代码中,我们只做了一点改变,我们返回的是一个函数,而不是函数的执行结果,所以最后要变成test1()()。那么大家可以看到执行的结果依然是0。这就说明了函数的作用域是在函数定义时就已经确定了,而不是在函数调用时确定的。因为如果是在调用时确定,那么最后执行test1()()这个函数,实际上是执行getCount()这个函数,而这个函数中会反回一个count,在当前的位置,能够拿到的count应该是全局变量count=5,打印结果应该是5,但是最后证明是0,所以就证明了上面那句话:在JavaScript中作用域是在函数定义的时候决定的,而不是函数调用时决定的。这就是闭包,在作用域外依然能够访问到局部变量,并且一直保持下去而不被垃圾回收(一般情况下,局部变量会在函数执行完后,被当做垃圾回收,而在全局域是不可能访问到这个局部变量的)。

    再举一个例子:

           function test4(){
                    var count=3;
                    return {
                        getCount:function(){
                            return count;
                        }
                    }
                }
                console.log(test4().getCount());//3

    我们在执行test4().getCount();这个语句时,执行环境是在全局域中,按理说我们是不可能拿到局部变量中的count值的,但是我们拿到了,这就是闭包的神奇之处,大家可能已经看到了,闭包中一定会return一个东西,可以直接是这个局部变量,也可以是获取这个局部变量的方法。那么闭包有什么用呢,其实我们可以用来保护某些变量,要想获取或者改变这个变量就必须通过我给定的方法来实现,有点类似于Java里面的setter和getter方法,下面我们来模拟一下。

               function test5(){
                    var count=6;
                    return {
                        getCount:function(){
                            return count;
                        },
                        setCount:function(val){
                            return count=val;
                        }
                    }
                }
                
                console.log(test5().getCount());  //6
                console.log(test5().setCount(9)); //9
                

    以上就是模拟的getter和setter方法,一般情况下我们可能去定义一个全局变量来达到这种效果,但是大家都知道这样很不安全,而这种方法就很好的保证了count的安全性,不会被轻易的篡改,因为你不通过setCount这个方法你根本碰不了它。

    特殊的例子,可能从里开始接触闭包开始就一定会碰到这个例子

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

    上面的代码打印的结果是10,我们期待的结果应该是5。我们的本意应该是这个数组中,依次装入了0~9,但是最后我们发现,这个数组中装的全是10,为什么会这样呢。

    当我们在执行arr[i]=function(){ return i;}时,并不是把i装入了数组中,而是把function(){return i;}这个函数装入了数组中,而这个函数并未有执行,怎么可能会return i呢!

    而当我们最后执行test2()[5]()这句话时本意是要取出数组的第六个元素,在这时我们才会执行function(){return i;}这个函数(把test2()[5]替换成function(){return i;});而此时的i早已经变成了10,说以test2()[0]~test2()[9]的值都会是10,如果要达到我们的本意应该做如下改动:

                function test3(){
                    var arr=[];
                    for(var i=0;i<10;i++){
                        arr[i]=(function(){
                            return i;
                        }())
                    }
                    return arr;
                }
                console.log(test2()[7]);//7

    我们运用一个及时函数,使每次function(){return i;}这个匿名函数立马执行掉,那么arr[i]装入的值就是对应的i值了(关于立即执行函数,大家可以去查看其它资料,也是JavaScript中相当重要的部分)。类似的例子还有。

    for(var i = 0; i < 10; i++) {
        setTimeout(function() {
            console.log(i);  
        }, 1000);
    }

    我们的本意是想在1s以后依次打印0~9这10个数字,而真实结果却是全打印的10。原因依然是function(){console.log(i);}这个匿名函数要在1s以后才会执行,不是立马执行,而到1S以后,i的值早已经变为了10,解决办法如下:

              for(var i = 0; i < 10; i++) {
                    (function(e) {
                        setTimeout(function() {
                            console.log(e);  
                        }, 1000);
                    })(i);
                }

    那么这样就会打印出0~9这10个数了。参见资料(JavaScript私密花园)。

    好了关于闭包目前就只能写这么多了,大家可以看看《JavaScript权威指南》《你不知道的JavaScript》和《JavaScript语言精粹》中对闭包的解释,看了之后应该有个比较好的理解。

  • 相关阅读:
    spring相关记录
    xshell不能连接VM中的ubuntu
    MySQL 获得当前日期时间(以及时间的转换)
    struts2 action获取ajax提交数据中文乱码问题
    Write operations are not allowed in read-only mode (FlushMode.NEVER/
    在Action中以Struts2的方式输出JSON数据
    javascript 对象数组排序
    期待2015
    Mysql 导出数据库和指定表中的数据
    Ajax跨域问题
  • 原文地址:https://www.cnblogs.com/djlxs/p/5407374.html
Copyright © 2011-2022 走看看