1. 程式人生 > >js-----閉包淺談(Closure)

js-----閉包淺談(Closure)

《開場白》
作為初級菜鳥的我,對與閉包知識,真的是一團迷霧,我這裡就先把我現階段對閉包的理解寫出來,先記一下,隨後對閉包理解深入了,再看看!

1.什麼是閉包?

菜鳥教程”上這樣總結到:閉包就是一個函式引用另一個函式的變數,因為變數被引用著所以不會被回收,因此可以用來封裝一個私有變數。這是優點也是缺點,不必要的閉包只會增加記憶體消耗。或者說閉包就是子函式可以使用父函式的區域性變數,還有父函式的引數。看起來還是雲裡霧裡的,接著看看下面的例子吧!

2.回顧一下區域性變數和全域性變數

首先必須理解Javascript特殊的變數作用域。閉包這裡牽扯到變數,來吧
1. 全域性變數

可以在任意位置訪問的量就叫全域性變數。在函式外通過var宣告的變數。沒有宣告就使用的變數,不管在這句話在哪裡,都是預設全域性變數的賦值 。
2. 區域性變數函式中用var定義的變數,只能在函式中訪問這個變數,函式外部訪問不了。

//函式內部可以直接讀取全域性變數。
 var age = 18;
  function f1(){
    alert(age);
  }
  f1(); // --------------------18
//在函式外部自然無法讀取函式內的區域性變數。
  function f2(){
    var num=99;
  }
  f2();
  alert(num); // ---num is not defined//無法讀取函式內的age

3.那麼如何從外部讀取區域性變數?

/*
可以利用函式內再宣告函式:內嵌函式
函式t2就被包括在函式t1內部,這時t1內部的所有區域性變數,對t2都是可見的。但是反過來就不行,t2內部的區域性變數,對t1就是不可見的。既然t2可以讀取t1中的區域性變數,那麼只要把t2作為返回值,我們不就可以在t1外部讀取它的內部變量了嗎!
*/
  function t1(){
        var age = 18;
        function t2(){
            alert(age);
        }
        return t2;
  }
 var tmp = t1();
 tmp();//----------------------------18

大部分的語言,t1被呼叫執行,則申請記憶體,並把其區域性變數age, push入棧,t1函式執行完畢,內部的區域性變數,隨著函式的退出而銷燬.因此age = 20 的區域性變數已經消失了;
但是在js中,age = 20 這個變數,卻被t2捕捉,即使t1執行完畢,通過t2仍然可以訪問t2依然可以訪問該變數。這也是實現上面的訪問區域性變數。

再來看一下,這個情形的過程,引入js閉包的知識點。
在js中,t1執行過程中又生成了t2,而作用域上來說,t2能訪問到age = 20,於是age = 20 不會消失,而是與返回的t1函式形成了一個環境包,這個包是t2的(把其周圍的變數環境形成了封閉的環境包 共同返回),即使t1執行完畢,通過t2仍然可以訪問t1依然可以訪問該變數,這就是閉包!!!

4.小桃花穿越故事

再來舉個例子看懂閉包,通俗易懂的(燕十八老師講過的一個例子)
故事梗概:2017年12月3日,雙胞胎姐妹大桃花妹妹的小桃花因意外,穿越清朝 那麼他的姐姐是誰?

function closure(){
    var sister = '大桃花';
    var me = function(){
        alert(sister);
    }
    return me;
}
function place(){
    var sister = '清朝大福晉';
    var girl = closure();
    girl();
}
place();//------------------------------大桃花

5.閉包計數器

閉包來維護一個別人汙染不到的變數 做計數器

 var fn = (function(){
    var cnt = 0;
    return function(){
        return ++cnt;
    }
 })();
alert(fn());//------------------1
alert(fn());//------------------2
alert(fn());//------------------3

因為我們在上面的程式碼return回去了函式,然後因為這個自執行函式被fn引用所以裡面的變數cnt並沒有因為這個自執行函式執完而銷燬,而是儲存到了記憶體中,所以我們多次列印fn()就成了1、2、3

6.經典案例

閉包的使用場景

//沒有使用閉包,實現不了點哪一個li,就顯示數字幾
window.onload = function(){
    var ul = document.getElementsByTagName("ul")[0];
    var li = ul.getElementsByTagName("li");
    for(var i=0;i<li.length;i++){
        li[i].onclick = function(){
            console.log(i); //不管我怎麼點都是返回都一樣
        }
    }
}
//用閉包來解決問題
window.onload = function(){
    var ul = document.getElementsByTagName("ul")[0];
    var li = ul.getElementsByTagName("li");
    for(var i=1;i<=li.length;i++){
        (function(i){
            li[i].onclick = function(){
                console.log(i); //點選第幾個返回第幾個
            }
        })(i)
    }
}

閉包用途
本質上,閉包就是將函式內部和函式外部連線起來的一座橋樑。閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函式內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。

《結束語》
聽說閉包用處很大(深入的理解JavaScript的閉包是邁向高階JS程式設計師的必經之路,理解了其解釋和執行機制才能寫出更為安全和優雅的程式碼),但是目前只掌握了這些,學習還是循序漸進的好,慢慢理解閉包吧!