JavaScript的閉包特性


 

       閉包是一個比較抽象的概念,尤其是對js新手來說。在這里,我就我個人的理解j簡單談一下:

     閉包:官方解釋是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。然而,當我看到這個官方解釋的時候頓時就覺得不一般,這個解釋太學術了,沒達到一定境界的人是理解不了其中深層次內涵的。為此,我們將舉出實例來初步說明js中的閉包特性。在了解閉包特性之前,我們需要補充了解變量的作用域。

一、變量的作用域

       在JS當中一個變量的作用域(scope)是程序中定義這個變量的區域。變量分為兩類:全局(global)變量和局部變量。其中全局變量的作用域是全局性的,即在JavaScript代碼中,它處處都有定義。而在函數之內聲明的變量,就只在函數體內部有定義。它們是局部變量,作用域是局部性的。函數的參數也是局部變量,它們只在函數體內部有定義。

       我們可以借助JavaScript的作用域鏈(scope chain)更好地了解變量的作用域。每個JavaScript執行環境都有一個和它關聯在一起的作用域鏈。這個作用域鏈是一個對象列表或對象鏈。當JavaScript代碼需要查詢變量x(如下圖)的值時(這個過程叫做變量解析(variable name resolution)),它就開始查看該鏈的第一個對象。如果那個對象有一個名為x的屬性,那么就采用那個屬性的值。如果第一個對象沒有名為x的屬性,JavaScript就會繼續查詢鏈中的第二個對象。如果第二個對象仍然沒有名為x的屬性,那么就繼續查詢下一個對象,以此類推。如果查詢到最后(指頂層代碼中)不存在這個屬性,那么這個變量的值就是未定義的。

     

例子1:js中的全局變量,結果為50       例子2:js中的局部變量,結果報錯,原因就                                                              是函數中的局部變量在外部是不能被引用      

           

      例子3:看看下面代碼,初一看好像結果是50,但是結果確實undefinde,我們來解釋說明下為什么是undefind----->

      作用域鏈圖中很明確的表示出:在變量解析過程中首先查找局部的作用域,然后查找上層作用域。在例子1中的函數當中沒有定義變量i,於是查找上層作用域(全局作用域),進而進行輸出其值。但是在例子2中的函數內定義了變量i(無論是在alter之后還是之前定義變量,都認為在此作用域擁有變量i),於是不再向上層的作用域進行查找,直接輸出i。然而在例子3中,函數內部依然定義了變量i,於是不會去查找全局作用於,然而不幸的是此時的局部變量i並沒有賦值,所以輸出的是undefined。

                               

二、閉包的概念

    上面官方解釋比較學術,我個人比較同意下面這個說法——>即函數定義和函數表達式位於另一個函數的函數體內。而且,這些內部函數可以訪問它們所在的外部函數中聲明的所有局部變量、參數和聲明的其他內部函數。當其中一個這樣的內部函數在包含它們的外部函數之外被調用時,就會形成閉包。也就是說,內部函數會在外部函數返回后被執行。而當這個內部函數執行時,它仍然必需訪問其外部函數的局部變量、參數以及其他內部函數。這些局部變量、參數和函數聲明(最初時)的值是外部函數返回時的值,但也會受到內部函數的影響。-----個人理解就是:閉包是能夠讀取其他函數內部變量的函數,即在外面可以調用函數中的函數的變量,其實他就是將函數內外部連接起來的橋梁。

如下面例子4:

這段代碼有以下兩個特點:

     1、函數b嵌套在函數a內部;2、函數a返回函數b;

引用關系如下圖:

 

          jsclosure

      這樣在執行完var c = a()后,變量c實際上是指向了函數b,b中用到了變量i,再執行c()后就會彈出一個窗口顯示i的值(第一次為1)。這段代碼其實就創建了一個閉包,為什么?因為函數a外的變量c引用了函數a內的函數b,就是說:

   當函數a的內部函數b被函數a外的一個變量引用的時候,就創建了一個我們通常所謂的“閉包”。

 

      當函數b執行的時候亦會像以上步驟一樣。因此,執行時b的作用域鏈包含了3個對象:b的活動對象、a的活動對象和window對象,如下圖所示:

          http://www.felixwoo.com/wp-content/uploads/attachments/200712/11_110522_scopechain.jpg

如圖所示,當在函數b中訪問一個變量的時候,搜索順序是:

  1. 先搜索自身的活動對象,如果存在則返回,如果不存在將繼續搜索函數a的活動對象,依次查找,直到找到為止。
  2. 如果函數b存在prototype原型對象,則在查找完自身的活動對象后先查找自身的原型對象,再繼續查找。這就是Javascript中的變量查找機制。
  3. 如果整個作用域鏈上都無法找到,則返回undefined。

三、閉包的用途及優勢

(一)、用途

     1、閉包可以讀取函數內部變量    2、將函數內部變量的值始終保存在內存中

例子5:

      這個例子中的result實際上就是閉包函數b,他一共運行兩次,第一次值99,第二次值為100,這就說明i一直在內存中,而不是在第一次a函數調用之后就自動清除。另外還需注意iAdd=function(){i++;},這里iAdd是全局變量,且它的值為匿名函數,其實也是一個閉包。

(二)、優勢

 

 1、保護函數內的變量安全。以最開始的例子為例,函數a中i只有函數b才能訪問,而無法通過其他途徑訪問到,因此保護了i的安全性。

 

 

 

 2、在內存中維持一個變量。依然如前例,由於閉包,函數a中i的一直存在於內存中,因此每次執行c(),都會給i自加1。

 

 3、通過保護變量的安全實現JS私有屬性和私有方法(不能被外部訪問)。

 

原文地址:http://my.oschina.net/longteng2013/blog/156782


免責聲明!

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



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