在上次的分享中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
在上面代碼中,分別用 let
和 var
聲明瞭兩個變量。而後在代碼塊以外調用這兩個變量,結果 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 }
完。
感謝你們的閱讀。