JavaScript——以簡單的方式理解閉包


      閉包,在一開始接觸JavaScript的時候就聽說過。首先明確一點,它理解起來確實不復雜,而且它也非常好用。那我們去理解閉包之前,要有什么基礎呢?我個人認為最重要的便是作用域(lexical scope),如果對作用域和作用域鏈不理解的同學最好自己先去學一學,再回過頭來,理解閉包,就更加輕松。

  下面便直接進入主題。

  我們知道一個函數是有作用域的,在函數內部定義的局部變量只有在函數內部才可以訪問的到。一旦函數訪問結束被銷毀,局部變量隨之也會銷毀,無法通過任何方式再次訪問局部變量,除了閉包。也就是說,我們可以通過閉包這一個方法,從函數的外部去訪問到函數內部的變量,即使那個函數已經被銷毀。沒錯,閉包最重要的用法就是,我們只提供某些接口去訪問和修改局部變量,而外部是不能直接訪問到局部變量的。

  說了那么多有關如何使用閉包,我們來看看閉包是長什么樣子的。又到了舉個栗子的環節,依然是最簡單的people和name。

var people = function(){
        var name = "Yika";
        var sayName = function(){
            return name;  //訪問了people函數的局部變量name
        }
        var setName = function(newName){
            name = newName;  //訪問了people函數的局部變量name
        }
        return{
            sayName: sayName,
            setName: setName
        }//返回一個對象
    }

    var p1 = people();        //函數return的是一個對象,這個對象里有兩個函數sayName和setName
    console.log(p1.name);     //undefined.   name是people函數里的局部變量,而不是p1對象的屬性,當然為undefined
    console.log(p1.sayName());//"Yika"
    p1.setName("Ruyi");       //通過setName函數修改局部變量name的值
    console.log(p1.sayName());//"Ruyi"

  看完這個例子,想必對閉包多少有個了解啦,除了注釋的內容,下面再做些補充。

  問:為什么局部變量name屬性在people執行完之后,沒有被銷毀呢,反而數值還保存在內存中。

  答: 在例子中,函數注釋那里專門寫了(訪問了people函數的局部變量name)。正是因為people函數里的sayName函數和setName函數訪問了name屬性,並且通過return傳到了p1對象里,成了p1的兩個方法。因為方法一直引用着people函數的局部變量,所以不會被消除,依然會在內存中。這樣便形成了閉包,可以在函數外部訪問到函數內部的局部變量。

  對此,我們可以換個更直觀的寫法。

var people = function(){
        var name = "Yika";

        var obj = {
            sayName: function(){
                return name;
            },
            setName: function(newName){
                name = newName
            }
        };

        return obj;       //直觀的返回對象
    }
//下面的結果是一樣的。

  當然閉包也不是一直那么好用,特別是在循環里。繼續舉例子

<body>
    <input type="button"/>
    <input type="button"/>
    <input type="button"/>
    <input type="button"/>
    <input type="button"/>
    <input type="button"/>
    <input type="button"/>
    <script type="text/javascript">
        var oBtn = document.getElementsByTagName('input');
        for(var i = 0, len = oBtn.length; i < len; i++){
            oBtn[i].onclick = function(){
                alert("value = " + i);    //閉包陷阱的發生地!永遠輸出 value = 7
            }
        }
    </script>
</body>

  當我們運行上面的代碼的時候,我們是這樣想的,循環一下按鈕,並輸出按鈕所在的序號,但是我們得到的卻永遠是7。其實用我們之前講的閉包會讓變量的值一直保存在內存中的原理想一想,就應該懂了。當我們循環的時候按鈕的點擊事件時,是引用了for循環里的 i 變量。當所有按鈕都綁定了點擊事件后,i 的值也已經變成了7,當然所有的按鈕輸出的都是7啦!怎么解決這個問題也很好辦的,但是我希望留下給大家思考。這里就說一下大致思路吧,我們可以在循環之外創建一個輔助函數,並讓輔助函數return一個綁定當前 i 的函數。

  當然這里我也只是拋磚引玉的介紹了一下閉包,希望可以幫到大家淺顯簡單的了解閉包。

  還是那句話噢,有問題立即指正,我一定會立馬檢查更正,以免誤導了大家!


免責聲明!

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



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