谈谈JS中的闭包

一、什么是闭包?

看概念总是迷迷糊糊,好像懂了,却又说不清。在此引用别的博主的话:

  通俗地讲就是别人家有某个东西,你想拿到但是因为权限不够(不打死你才怪),但是你可以跟家里的孩子套近乎,通过他拿到!

  这个家就是局部作用域,外部无法访问内部变量,孩子是返回对象,对家里的东西有访问权限,借助返回对象间接访问内部变量!

  

总结下有三个特点:

  • 函数嵌套函数

  • 内部的函数可以引用外部函数的参数或者变量

  • 参数和变量不会被垃圾回收机制回收,因为内部函数还在引用

        function aaa(){
            var a = 5;
            function bbb(){
                console.log(a);
            }
            return bbb;
        }
        var c = aaa();     //此时c是aaa内部return的bbb函数体,外部函数aaa已运行完毕,但是变量仍被内部函数引用,故不会释放。
        c();   //打印结果是5

  

二、闭包的好处

  • 变量可以长期驻扎在内存之中

  • 避免全局变量的污染,有私有成员

下面看一下例子:

1、普通函数调用:aaa执行完毕,就回收a变量,再次执行,重新赋值计算。

        function aaa(){
            var a = 1;
            a++;
            alert(a);
        }
        aaa(); //2
        aaa(); //2

2、闭包方式调用:aaa执行后,由于a变量还在被内部函数引用,故不会被回收,再次计算,在上一次的结果上进行累加。

        function aaa(){
            var a = 1;
            return function(){
                a++;
                alert(a);
            }
        }
        var c = aaa();
        c(); //2
        c(); //3

3、上面还可以改写成:

        var aaa = (function(){
            var a = 1;
            return function(){
                a++;
                alert(a);
            }
        })()  //函数表达式自执行,结果是内部函数体
        aaa(); //2
        aaa(); //3

三、闭包的用途

  • 模块化代码

        //aaa即是一个模块,aaa里的私有变量是a,私有函数是bbb和ccc,外部访问不到,但可以通过返回一个对象让外部访问。
var aaa = (function(){ var a = 1; function bbb(){ a++; alert(a); } function ccc(){ a++; alert(a); } return { b:bbb, c:ccc } })() //函数表达式自执行,结果是return后面的对象 aaa.b(); //2 aaa.c(); //3
  • 在循环中找到索引
<body>
    <ul>
        <li>111111</li>
        <li>111111</li>
        <li>111111</li>
    </ul>
    <script>
        window.onload = function(){
            var aLi = document.getElementsByTagName('li');
            for(var i=0;i<aLi.length;i++){
                aLi[i].onclick = function(){
                    alert(i); //顺序点击三个li,分别弹出3 3 3。因为点击的时候for循环已经执行完毕
                }
            }
        }
    </script>
</body>

  可使用闭包改为:

<body>
    <ul>
        <li>111111</li>
        <li>111111</li>
        <li>111111</li>
    </ul>
    <script>
        window.onload = function(){
            var aLi = document.getElementsByTagName('li');
            for(var i=0;i<aLi.length;i++){
                (function(i){
                    aLi[i].onclick = function(){
                        alert(i); //0 1 2
                    }
                })(i) //将i作为参数传递给内部函数,i 在for循环执行完毕不会被释放
            }
        }
    </script>
</body>

  或者另一种写法:

<body>
    <ul>
        <li>111111</li>
        <li>111111</li>
        <li>111111</li>
    </ul>
    <script>
        window.onload = function(){
            var aLi = document.getElementsByTagName('li');
            for(var i=0;i<aLi.length;i++){
                aLi[i].onclick = (function(i){
                    return function(){
                        alert(i); 
                    }
                })(i) //循环的时候,这个函数表达式自执行,i也是驻扎在内存当中。
            }
        }
    </script>
</body>

  

四、闭包需要注意的问题

  • 在IE下会引发内存泄露

<body>
    <div id="div1">my div</div>
    <script>
        window.onload = function(){
            var oDiv = document.getElementById('div1');
            oDiv.onclick = function(){
                alert(oDiv.id);
            }
        }
    </script>
</body>

    当一个变量为一个dom节点获取或者宿主对象(此例子中为oDiv),它的一个属性(此例中为onclick)引用一个内部函数,而内部函数又在引用外部对象(此例中外部对象是oDiv)。会造成互相引用,引起内存泄露。

  • 如何解决:

    方法一:

<body>
    <div id="div1">my div</div>
    <script>
        window.onload = function(){
            var oDiv = document.getElementById('div1');
            oDiv.onclick = function(){
                alert(oDiv.id);
            }

            //卸载文档时候执行
            window.onunload=function(){
                oDiv.onclick = null;
            }
        }
    </script>
</body>

    方法二:

<body>
    <div id="div1">my div</div>
    <script>
        window.onload = function(){
            var oDiv = document.getElementById('div1');
            //赋值给一个变量
            var oId = oDiv.id;
            oDiv.onclick = function(){
                alert(oId);
            }
            //还要让对象对空
            oDiv = null;
        }
    </script>
</body>

  

猜你喜欢

转载自www.cnblogs.com/jelina/p/11146345.html