深刻理解JavaScript系列:試着談談閉包

  閉包多是JavaScript裏最被人神乎其神的一個概念,世間萬物皆凡夫俗子,你覺着他神奇是由於你根本沒有了解,全部的事物當你瞭解透徹後就不會有這種不明覺厲的錯覺了。哈哈哈,上來又是一頓哲學普及。javascript

  下面開始進行闡述。java

  1.什麼是閉包?web

  參閱了不少資料,最後比較靠譜的解釋就是「閉包是一個函數」。瀏覽器

  閉包是一個函數,什麼樣的函數才能叫作閉包?能夠從他的做用上理解閉包就是可以讀取其餘函數內部變量的函數。閉包

  2.閉包是怎麼造成的?函數

  首先再回顧下JavaScript的做用域這個概念。JavaScript是函數做用域而不是塊狀做用域,也就是說JavaScript的做用域是以函數來劃分的,函數內部的變量爲私有變量,而相對的全局變量是指在全部的函數外部定義的變量。全局變量自動歸爲全局對象global的屬性,在web瀏覽器中這個global對象由window對象來承擔這個全局變量的角色。性能

  通常的,在函數內部聲明的加了var聲明符的(函數內部定義變量不加var的話會默認聲明爲全局變量,爲了不全局空間變量污染因此要謹記)變量,在這個函數執行時候建立該函數相關的做用域,函數固然會有嵌套,因此就會存在做用域連接做用域的問題,也就是所說的做用域鏈。這個做用域鏈從最裏層函數做用域開始,逐漸向外層連接,一層一層直到最外層的全局做用域。因此在內層的函數使用某個變量的時候會按照這個連接順序去尋找,若是中途找到則中止,直到全局做用域尚未找到則該變量爲未定義。this

  正常狀況,一個函數在調用開始執行時建立這個函數執行上下文及相應的做用域鏈,在函數執行結束後釋放函數執行上下文及相應做用域鏈所佔的空間。對象

  明白了做用域後再來看閉包怎樣才能讀取到其它函數的內部變量這個問題。首先,若是是函數A嵌套函數B,函數B天然能夠訪問函數A裏的變量,向上還能夠直到全局做用域的變量。這種狀況確定造成不了閉包。因此閉包是在某函數A以外的函數B想要訪問函數A內部的變量下造成的。blog

  3.閉包怎麼樣才能讀取到其它函數的內部變量

  最多見的形式就是在函數內部返回一個函數,這個返回的函數有引用外部函數的變量。示例代碼以下:

function foo() {
    var tmp = 0;
    return function () {
        console.log(tmp = tmp + 1);
    }
}
var bar = foo(); // foo()執行後返回內部匿名函數,因此bar如今就是內部的那個函數的引用
bar(); //如今執行bar,也就是執行內部的那個匿名函數,輸出爲1,
bar(); //緊接着再執行bar,輸出爲2,也就是說foo函數內部的tmp變量不只被外部的函數訪問到了,並且tmp的值沒有被清空,會累加

  這就知足了造成閉包的條件,即相對函數A(這裏指foo())而言他外部的函數B(這裏指bar(),其實bar只是內部匿名函數的引用在外部執行)訪問到了A做用域的變量tmp,並且值的注意的是tmp變量一直存在於內存中。

  依據上邊這段代碼解釋爲何變量會一直存在於內存中而不是隨着函數執行完畢後就被垃圾回收。

  首先,bar是在外部做用域中,大部分時候是在全局做用域中,而全局做用域中的變量是一直存在於內存中的,因此當bar引用foo的時候,間接地foo也會一直存在於內存中,既然foo都一直存在於內存中,那foo所造成的函數做用域裏的一切都會一直存在於內存中,所以就會出現上邊看到的訪問到的內部變量不會隨着函數執行完後進行垃圾回收的狀況。

  4.怎樣才能理解閉包?

  首先明白他是一個能訪問其它函數內部做用域變量的函數,打破了常規的函數做用域的限制,再理解就是他引用的變量會一直存在於內存中。

  下面貼一些閉包的代碼,都是從其餘地方看到的:

var object = {
        a: 1,
        getA: function () {
            console.log(this.a=this.a+1);
        }
    }
object.getA();//輸出爲2
object.getA();//輸出爲3

  

var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();

  5.閉包都有什麼用處? 

  1)一個是前面提到的能夠讀取函數內部的變量
  2)另外一個就是讓這些變量的值始終保持在內存中。
   6.使用閉包須要注意的地方有哪些?
  1)因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。
  2)閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。
 
  完!祝好運。
相關文章
相關標籤/搜索