深刻理解javascript閉包(二)

 

 

在上次的分享中javascript--函數參數與閉包--詳解,對閉包的解釋不夠深刻。本人通過一段時間的學習,對閉包的概念又有了新的理解。因而便把學習的過程整理成文章,一是爲了加深本身閉包的理解,二是給讀者提供學習的途徑,避免走彎路。javascript

 

如下的分享會分爲以下內容:html

 

1.let命令java

2.閉包特色的解讀閉包

3.循環中的閉包函數

 

 

 

1.let命令post

  在講閉包前,有必要談談ES6中的新概念,let命令。由於在贅述循環中的閉包時會使用到let命令。性能

  基本用法學習

  ES6新增了let命令,用來聲明變量。它的用法相似於var,可是所聲明的變量,只在let命令所在的代碼塊內有效。url

1     if (true) {
2         var a = 1;
3         let b = 2;
4     }
5     console.log(a); // 1
6     console.log(b); // ReferenceError: b is not defined

  在javascript--函數參數與閉包--詳解中,談到在局部變量只能在函數內部聲明,在其餘代碼(如 if 條件語句,for循環語句)用 var 聲明的變量都爲全局變量。spa

  在上面代碼中,分別用 letvar 聲明瞭兩個變量。而後在代碼塊以外調用這兩個變量,結果 let 聲明的變量報錯,var 聲明的變量返回了正確的值。這代表,if 條件語句中使用var聲明的變量爲全局變量,能夠在全局做用域下訪問。而 let 聲明的變量只在它所在的代碼塊有效,在全局做用域下沒法訪問。

  再來看看這兩個例子。

1     for (let i = 0; i < 10; i++) {}
2     console.log(i);  //ReferenceError: i is not defined
1     for (var i = 0; i < 10; i++) {}
2     console.log(i);  // 10

 

 

2.閉包特色的解讀

  咱們知道,閉包有三個特色

  a:在一個函數內部定義另一個函數,並返回內部函數或當即執行內部函數。

  b:內部函數能夠訪問外部函數定義的局部變量 (變量採用var聲明)

  c:讓局部變量始終保存在內存中。也就是說,閉包能夠使得它誕生的環境一直存在。

  咱們來看一個例子,嘗試串起這三個特色。

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

  首先,在函數keith內部返回了一個匿名函數,若是函數keith沒有返回值,則默認返回值爲undefined。

  而後,由於在函數keith中返回了一個匿名函數,又把調用函數keith的結果賦值給了全局變量result,因此全局變量result是一個閉包。當連續調用result時,依次返回1,2,3。返回值說明了內部函數能夠訪問外部函數定義的局部變量。也就是說,閉包記住了外部函數定義的局部變量的調用結果。

  最後,由於咱們把一個閉包賦值給了一個全局變量result,在調用時依次輸出1,2,3。說明了在函數keith外部訪問的這個局部變量a一直存在全局做用域中。也就是說,局部變量 a 一直存在於內存當中,因此不會被垃圾回收機制回收。

 

  因此使用閉包的時候的注意點:

  因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。解決方法是,在退出函數以前,將不使用的局部變量所有刪除。

 

 

3.循環中的閉包

  一個常見的錯誤出如今循環中使用閉包,假設咱們須要在每次循環中調用循環序號。

1     for (var i = 0; i < 10; i++) {
2         setTimeout(function() {
3             console.log(i); //10
4         }, 1000)
5     }

  上面代碼中,不會符合咱們的預期,輸出數字0-9。而是會輸出數字10十次。

  出現錯誤的緣由在於咱們在setTimeout函數裏面定義了一個匿名函數,匿名函數的做用是在控制檯輸出變量 i,而變量 i 是一個全局變量,在全局範圍內都有效。因此每一次循環,新的 i 值都會覆蓋舊值,致使最後輸出的是最後一輪的i的值。

  因此,針對循環中的閉包,有如下兩種解決方法。

  一是使用當即執行函數(IIFE),並把 i 做爲它的參數,此時函數內 e 變量就擁有了 i 的一個拷貝。當傳遞給 setTimeout 的匿名函數執行時,它就擁有了對 e 的引用,而這個值是不會被循環改變的。

1     for (var i = 0; i < 10; i++) {
2         (function(e){
3             setTimeout(function() {
4                 console.log(e); //1,2,3,....,10
5             }, 1000)
6         })(i)
7     }

  二是讓變量 i 只在代碼塊中有效。也就是說讓其成爲局部變量。變量 i let 聲明的,當前的 i 只在本輪循環有效,因此每一次循環的 i 其實都是一個新的變量,因此最後輸出的是1,2,3,4....,10。

1     for (let i = 0; i < 10; i++) {
2         setTimeout(function() {
3             console.log(i); //1,2,3...,10
4         }, 1000)
5     }

  

 

 

 

完。

 

感謝你們的閱讀。

轉載請註明出處:http://www.cnblogs.com/Uncle-Keith/p/5801015.html

相關文章
相關標籤/搜索