我所理解的JavaScript闭包

1、闭包(Closure)

1.一、什么是闭包?

理解闭包概念:css

a、闭包是指有权限访问另外一个函数做用域的变量的函数,建立闭包的常见方式就是在一个函数内部建立另外一个函数,也就是建立一个内部函数,建立一个闭包环境,让返回的这个内部函数保存要引用的变量,以便在后续执行时能够保持对这个变量的引用。html

b、只要存在调用内部函数的可能,JavaScript就须要保留被引用的函数。并且JavaScript运行时须要跟踪引用这个内部函数的全部变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间。java

c、Javascript语言特有的"链式做用域"结构(chain scope),子对象会一级一级地向上寻找全部父对象的变量。安全

d、若是一个内部函数被调用且引用了它的外部变量那么它就是一个闭包。闭包

 

相信你看了上面的这段话可能还不理解什么是闭包,那么我就举一个闭包的经典例子来帮助你理解闭包的概念吧。函数

请看下面这段代码: post

复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>闭包</title>
    </head>
    <body>
        <script type="text/javascript">
            function out() {
                var i = 0;
                function inner() {
                    alert(++i);
                }
                return inner;
            }
            var  ref= out();
            ref();
        </script>
    </body>
</html>
复制代码

结果:性能

上面的代码有两个特色:
一、建立两个函数out,inner,函数inner嵌套在函数out内部,也能够说是函数inner是函数out的内部函数;
二、调用函数out返回内部函数inner。
这样在执行完var ref=out()后,变量ref其实是指向了函数inner,也能够说是引用了函数inner,再执行ref()后就会看到上图弹出一个窗口显示i的值第一次为1。ui

这段代码其实就建立了一个闭包,为何?由于函数out外的变量ref引用了函数out内部的函数inner,也就是说:

当函数out的内部函数inner被函数out外的一个变量ref引用的时候,就建立了一个闭包。

可能你仍是不理解闭包这个概念,由于你不知道闭包有什么用,那么先理解一下闭包的做用吧。

1.二、为何要用闭包(做用)?

1.2.一、保护函数内的变量安全。

解释:以上面的的例子为例,函数out中i只有函数innder才能访问,而没法经过其余途径访问到,所以保护了i的安全性。

1.2.二、经过访问外部变量,一个闭包能够暂时保存这些变量的上下文环境,当引用完毕后才会销毁。

在上面的示例中增长了一次函数ref()的调用,执行的结果以下:

解释:当函数out执行完而且最终退出时,它的局部变量会被Javascript的垃圾回收机制回收所占用的资源,也就是局部变量被销毁,可是由于建立了闭包环境,那么内部函数inner就会暂时保存外部函数的局部变量上下文环境。不会被垃圾回收机制回收。因此函数out中的i被复制一份暂时保存下来,这样每次执行ref(),i都是自加1后alert输出i的值。当变量ref引用内部函数inner完成结束后,最后才会被回收机制回收。这只是我对闭包做用的简单初浅理解,不专业也不严谨,但大概意思就是这样,理解闭包须要按部就班的过程。

相信你看了闭包的做用,对理解什么是闭包是否更明白一些,若是仍是很疑惑,那么我就再举几个闭包的经典例子来帮助你理解闭包的概念吧。

1.三、闭包的经典示例

1.3.一、示例一

问题:假如咱们有以下需求请在页面中放10个div,每一个div写上对应的数字,当点击每个div时显示索引号,如第1个div显示0,第10个div显示9。

可能你会这样作:

复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>闭包</title>
        <style type="text/css">
            div {
                width: 50px;
                height: 50px;
                background: lightcoral;
                float: left;
                margin: 20px;
                font: 15px/50px "microsoft yahei";
                text-align: center;
            }
        </style>
    </head>
    <body>
        <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>div-7</div>
        <div>div-8</div>
        <div>dvi-9</div>
        <div>div-10</div>
        <script type="text/javascript">
            var divs=document.getElementsByTagName("div");
            for (var i=0;i<divs.length;i++) {
                divs[i].onclick=function(){
                    alert(i);
                }
            }
        </script>
    </body>
</html>
复制代码

 结果:

从上面的结果你会发现,无论你点击了哪一个div,弹出的框div索引老是10,这可能会让你很意外,会产生疑惑,为何会出现这样的结果呢?

解释:由于点击事件的函数内部使用外部的变量i一直在变化,当咱们指定click事件时并无保存i的副本,这样作也是为了提升性能,但达不到咱们的目的,咱们要让他执行的上下文保存i的副本,这种机制就是闭包。

使用闭包能够解决此问题,代码作了以下修改:

复制代码
<script type="text/javascript">
            var div=document.getElementsByTagName("div");
                for (var i = 0; i < div.length; i++) {
                    div[i].onclick=function(n){
              return function(){ alert(n);//产生闭包,引用外部变量。 } }(i); } </script>
复制代码

结果: 

 

从上面的结果你会发现,使用闭包后,就达到你预期的结果了。

解释:n是外部函数的值,可是内部函数须要使用这个值,由于产生闭包执行环境,因此返回函数前的n被临时驻留在内存中给点击事件使用,简单说就是函数的执行上下文被保存起来,i生成了多个副本。

 1.3.二、示例二

 示例代码:

复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <script type="text/javascript">
            function out() {
                var i = 10;
                return function inner() {
                    i++;
                    alert(i);//引用了外部变量,建立了闭包环境
                };
            }
            //此处为函数调用,第一个括符为调用out方法,第二个括符为调用返回的inner方法。
            out()();
        </script>
    </body>
</html>
复制代码

 

运行 结果:

1.3.三、示例三

问题:不使用闭包的状况下,无论执行多少次,结果都同样是3.

示例代码:

复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>闭包</title>
    </head>
    <body>
        <script type="text/javascript">
            var arr = [1, 2, 3];
            var obj = {};
            var exp = function() {
                for(var i = 0; i < arr.length; i++) {
                    obj[i] = function() {
                        console.log(i);
                    };
                }
            }
            exp();
            var fn0 = obj[0];
            var fn1 = obj[1];
            var fn2 = obj[2];
            fn0(); //输出3
            fn1(); //输出3
            fn2(); //输出3
        </script>
    </body>
</html>
复制代码

运行结果:

使用闭包后的示例代码:

复制代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>闭包</title>
    </head>
    <body>
        <script type="text/javascript">
            var arr = [1, 2, 3];
            var obj = {};
            var exp = function() {
                for(var i = 0; i < arr.length; i++) {
                    (function(i) {
                        obj[i] = function() {
                            console.log(i);
                        };
                    })(i); //i做为参数传给当即调用函数
                }
            };
            exp();
            var fn0 = obj[0];
            var fn1 = obj[1];
            var fn2 = obj[2];
            fn0(); //输出0
            fn1(); //输出1
            fn2(); //输出2
        </script>
    </body>
</html>
复制代码

运行结果:

 

 上面代码解释:

  • 在for循环里建立了一个当即调用函数表达式
  • fn0,fn1,fn2分别指向了匿名函数的引用
  • fn0(),fn1(),fn2()都访问了i(这个i是位于这个匿名函数的上层做用域链,它会被保存在内存中,对于每个函数引用来讲i是惟一的)

总结:

相信经过以上是几个闭包示例,你对闭包也有必定的理解了吧。限于本人才疏学浅,对闭包的理解也并非很透彻,只是理解了一些表面,会使用而已。

若你想理解的更深刻推荐你去看老外对闭包的解释:http://stackoverflow.com/questions/111102/how-do-javascript-closures-work

 

做者: 欲泪成雪
QQ:1521274639 点击这里给我发消息
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文连接,不然保留追究法律责任的权利。
相关文章
相关标签/搜索