談談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

三、閉包的用途

  • 模塊化代碼


var aaa = (function(){ //aaa是一個模塊,私有變量是a,私有函數是bbb和ccc 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>

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM