js----深入理解閉包

來源:互聯網
上載者:User

標籤:引用   for   應該   比較   對象   操作   不能   保留   列印   

閉包算是js裡面比較不容易理解的點,尤其是對於沒有編程基礎的人來說。

其實閉包要注意的就那麼幾條,如果你都明白了那麼征服它並不是什麼難事兒。下面就讓我們來談一談閉包的一些基本原理。

 

閉包的概念


一個閉包就是一個函數和被建立的函數中的範圍對象的組合。(範圍對象下面會說)

通俗一點的就是 “ 只要一個函數中嵌套了一個或多個函數,那麼我們就可以稱它們構成了閉包。 ”

類似這樣:

1 function A() {2   var i = 5;3   return function() {4     console.log(‘i = ‘+i);5   }6 }7 8 var a = A();9 a(); // i = 5

 

 

閉包的原理

 

  1、外部函數的局部變數若會被閉包函數調用就不會在外部函數執行完畢之後立即被回收。

  我們知道,不管什麼語言,作業系統都會存在一個記憶體回收機制,將多餘分配的空間回收掉以便減小記憶體。而一個函數的生命週期的是從調用它開始的,在函數調用完畢的時候函數內部的局部變數等都會被回收機制回收。

  我們拿上述例子來說,當我們的外部函數A調用完畢時,A中的局部變數i按理說就會被作業系統回收而不存在,但是當我們用了閉包結果就不是那樣了,i並不會被回收。試想,如果i被回收了那麼返回的函數裡面豈不是就是列印undefined了?

  i為什麼沒有被回收?

  在javascript執行一個函數的時候都會建立一個範圍對象,將函數中的局部變數(函數的形參也是局部變數)儲存進去,伴隨著那些傳入函數的變數一起被初始化。

  所以當調用A的時候就建立了一個範圍對象,我們姑且稱之為Aa,那麼這個Aa應該是這樣的: Aa { i: 5; };  在A函數返回一個函數之後,A執行完畢。Aa對象本應該被回收,但是由於返回的函數使用了Aa的屬性i,所以返回的函數儲存了一個指向Aa的引用,所以Aa不會被回收。

  所以理解範圍對象,就能理解為什麼函數的局部變數在遇到閉包的時候不會在函數調用完畢時立即被回收了。

  再來個例子:

1 function A(age) {2   var name = ‘wind‘;3   var sayHello = function() {4     console.log(‘hello, ‘+name+‘, you are ‘+age+‘ years old!‘);5   };6   return sayHello;7 }8 var wind = A(20);9 wind();  // hello, wind, you are 20 years old!

  你能說出的它的範圍對象Ww是什麼嗎?

  Ww{ age: 20; name: ‘wind‘; };

 

  2、每調用一次外部函數就產生一個新的閉包,以前的閉包依舊存在且互不影響。

  3、同一個閉包會保留上一次的狀態,當它被再次調用時會在上一次的基礎上進行。

  每調用一次外部函數產生的範圍對象都不一樣,你可以這樣想,上面的例子,你每次傳入的參數age不一樣,所以就每次產生的對象不一樣。

  每調用一次外部函數那麼就會產生一個新的範圍對象。

 1 function A() { 2   var num = 42; 3   return function() { console.log(num++); } 4 } 5 var a = A(); 6 a(); // 42 7 a(); // 43 8  9 var b = A();  // 重新調用A(),形成新閉包10 b(); // 42         

  這個代碼讓我們發現了兩個事情,一、當我們連續調用兩次a();,num會在原基礎上自加。說明同一個閉包會保留上一次的狀態,當它被再次調用時會在上一次的基礎上進行。 二、我們的b();的結果為42,說明它是一個新的閉包,並且不受其他閉包的影響。

  我們可以這樣想,就好比我們吹肥皂泡一樣,我每次吹一下(調用外部函數),就會產生一個新的肥皂泡(閉包),多個肥皂泡可以同時存在且兩個肥皂泡之間不會相互影響。

 

  4、在外部函數中存在的多個函數 “ 同生共死 ”

  以下三個函數被同時聲明並且都可以對範圍對象的屬性(局部變數)進行訪問與操作。

var fun1, fun2, fun3;function A() {  var num = 42;  fun1 = function() { console.log(num); }  fun2 = function() { num++; }  fun3 = function() { num--; }  }A();fun1();   // 42fun2(); fun2(); fun1();    // 44fun3(); fun1();   //43var old = fun1;A();  fun1();   // 42old();   // 43   上一個閉包的fun1()

  由於函數不能有多個傳回值,所以我用了全域變數。我們再次可以看出在我們第二次調用A()時產生了一個新的閉包。

 

 

當閉包遇到迴圈變數

  當我們說到閉包就不得不說當閉包遇到迴圈變數這一種情況,看如下代碼:

 1 function buildArr(arr) { 2     var result = []; 3     for (var i = 0; i < arr.length; i++) { 4         var item = ‘item‘ + i; 5         result.push( function() {console.log(item + ‘ ‘ + arr[i])} ); 6     } 7     return result; 8 } 9 10 var fnlist = buildArr([1,2,3]);11 fnlist[0]();  //  item2 undefined12 fnlist[1]();  //  item2 undefined13 fnlist[2]();  //  item2 undefined

  怎麼會這樣呢?我們預想的三個輸出應該是 item0 1,  item1 2,  item2 3。為什麼結果卻是返回的result數組裡面儲存了三個 item2 undefined ?

  原來當閉包遇到迴圈變數時都是迴圈結束之後統一儲存變數值,拿我們上面的例子來說,i是迴圈變數,當迴圈全部結束的時候i正好是i++之後的3,而arr[3]是沒有值的,所以為undefined,有人會疑惑:為什麼item的值是item2,難道不應該是item3嗎?注意,在最後一次迴圈的時候也就是i = 2的時候,item的值為item2,當i++,i = 3迴圈條件不滿足迴圈結束,此時的item的值已經定下來了,所以此時的arr[i]為arr[3],而item為item2。這樣能理解嗎?如果我們將代碼改成這樣那就說得通了:

function buildArr(arr) {    var result = [];    for (var i = 0; i < arr.length; i++) {         result.push( function() {console.log(‘item‘ + i + ‘ ‘ + arr[i])} );    }    return result;}var fnlist = buildArr([1,2,3]);fnlist[1]();  //  item3 undefined

  那麼問題來了,如何改正呢?且看代碼:

 1 function buildArr(arr) { 2     var result = []; 3     for (var i = 0; i < arr.length; i++) { 4         result.push( (function(n) { 5            return function() { 6               var item = ‘item‘ + n; 7               console.log(item + ‘ ‘ + arr[n]); 8            } 9         })(i));10     }11     return result;12 }13 14 var fnlist = buildArr([1,2,3]);15 fnlist[0]();  //  item0 116 fnlist[1]();  //  item1 217 fnlist[2]();  //  item2 3

  我們可以用一個自執行函數將i綁定,這樣i的每一個狀態都會被儲存,答案就和我們預期的一樣了。

  所以以後在使用閉包的時候遇到迴圈變數我們要習慣性的想到用自執行函數來綁定它。

 

  以上就是我對閉包的理解,如果有有什麼意見或建議希望我們能在評論區多多交流。感謝,共勉。

js----深入理解閉包

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.