學了JavaScript有一段時間了,但是對閉包還是不太理解,於是懷著心中的疑問做了幾個小實驗,終於有點明白了。 首先看一下MDN上的定義:閉包是函數和聲明該函數的詞法環境的組合。 簡單來說,閉包是一種現象。 我在搞清楚了2個概念後,理解了閉包。 首先是關於函數以及函數調用的概念: 我們來做一個簡單 ...
學了JavaScript有一段時間了,但是對閉包還是不太理解,於是懷著心中的疑問做了幾個小實驗,終於有點明白了。
首先看一下MDN上的定義:閉包是函數和聲明該函數的詞法環境的組合。
簡單來說,閉包是一種現象。
我在搞清楚了2個概念後,理解了閉包。
首先是關於函數以及函數調用的概念:
我們來做一個簡單的實驗:
1 function foo () { 2 var a = 1; 3 function bar () { 4 console.log(a) 5 } 6 return bar; 7 } 8 9 var first = foo(); 10 console.log(first); 11 12 // 顯示結果: 13 // ƒ bar () { 14 // console.log(a) 15 // }
作為一個初學者,有一些小細節很容易干擾我的判斷。
首先要理解的是function f () {}是在聲明一個函數,要執行這個函數必須用f()進行調用。
f()調用函數後,js引擎會執行函數中的代碼,如果函數最後有寫return ...函數執行完畢才有返回值。
1 var first = foo();
表示把函數foo執行完畢後的返回值賦值給first,函數foo執行的操作是聲明瞭一個變數a,以及一個函數bar,然後把函數bar作為返回值返回。註意:不是把bar的執行結果返回,而是把這個函數本身返回。所以first就指向了函數bar。
其次是關於執行環境與作用域的概念
作用域的定義:作用域是一套規則,用於確定在何處以及如何查找標識符。
我對作用域的理解是:代碼起作用的範圍。
執行環境的定義:執行環境定義了變數或函數有權訪問的其他數據。每個執行環境都有三個重要的屬性,變數對象(Variable object,VO)、作用域鏈(Scope chain)和 this。環境中定義的所有變數和函數都保存在變數對象中
我對執行環境的理解:存儲一塊代碼中定義的所有的變數和函數的值。
因為全局作用域範圍最廣,所以函數內部也可以訪問到全局變數。
函數內部代碼執行的時候,發現某個變數的值在自己的執行環境中沒有,就需要去上級執行環境中找。函數執行完畢後,當前函數的執行環境就被銷毀,所以上級函數沒有辦法訪問內部函數中聲明的變數。
第二個小實驗:
1 function foo () { 2 var a = 1; 3 function bar () { 4 console.log(a) 5 } 6 return bar; 7 } 8 9 var first = foo(); 10 console.dir(first);
顯示結果如圖所示:
first就是函數bar,它能訪問自己的作用域,上級foo的作用域,以及全局作用域。
當js執行first的時候,first能訪問自己的作用域,上級foo的作用域,以及全局作用域。這種現象就是閉包。
閉包最大用處有兩個:一個是前面提到的可以讀取函數內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。所以我們就可以利用閉包來創建模塊機制:
1 function count() { 2 var number = 0; 3 return{ 4 add : function(x){ 5 if(x==undefined) { 6 number += 1; 7 }else{ 8 number +=x; 9 } 10 }, 11 reduce: function(x) { 12 if(x==undefined) { 13 number -= 1; 14 }else{ 15 number -=x; 16 } 17 }, 18 times: function() { 19 return number; 20 } 21 } 22 }
我們可以創建複數個加法器,而互不影響。
我關於閉包的理解就到這裡了,如果你還不明白,就自己動手寫代碼試驗吧!