網絡上關於做用域及閉包的文章不少,本身對於純理論知識並不能很快的理解,但本身對於圖畫有很強的記憶和理解能力,所以決定將此知識點以圖畫的知識表現出來,加深自身理解的同時若是能幫到正在學習的童鞋就再好不過了git
下面我以函數的整個生命週期來訴說此部分知識github
先寫一下示例代碼數組
var a = 10; function func(a) { var a = 20; a++; console.log(a); } func(); console.log(a);
引用類型在其中只能存儲地址,這個在此筆記談談值傳遞中有詳細說明瀏覽器
函數的執行環境出棧,AO 釋放,AO 中的局部變量一同被釋放掉。網絡
咱們得知整個結果以後,天然而然那兩個 console
的結果也顯然意見。閉包
前面咱們提到過,全局變量是可重用可是污染全局,局部變量不會污染全局可是不可重用。函數
我本身認爲閉包就是重用變量又保護變量不被污染的機制,就是爲了解決這一狀況而生的。工具
包裹受保護的變量和操做變量的內層函數的外層函數學習
外層函數要返回內層函數的對象spa
return function(){..}
function
return [function function function]
或 return {fun:function(){...}}
調用外層函數,用外部變量接住返回的內層函數對象,造成閉包。
先貼出示例代碼
function outer() { var num = 1; return function() { console.log(num++); }; } var getNum = outer(); getNum(); getNum(); num = 1; getNum();
下面我把閉包造成的原理用畫圖工具畫出來
window 中存入 outer 名並指向 outer 函數對象,getNum
由於聲明提早也先將變量名存在 window 中。
getNum = outer()
其實包含 outer 的建立和 getNum
的賦值。
上面的圖畫的是 outer 函數進行到 var num = 1;
,前面都有說過,不過多重複。
建立了匿名函數,getNum
指向了匿名函數對象,匿名對象的 scope 指向它的父級做用域,也就是 outer 的做用域,那這樣就造成了圖中的三角關係,此時 outer 執行完畢,離開 ECS 執行環境,outer 的 AO 本也應該隨着離開,可是由於這強大的三角關係,強行拉住不讓其釋放,也就造成了所謂的閉包。
那其實閉包的緣由就是:外層函數的做用域對象沒法釋放
getNum=outer()
getNum 其實就是一個函數
調用getNum()
,會生成 getNum
的臨時做用域,圖中可看出,getNum
其實就是在 outer 中的匿名函數,因此他的 parent 就指向 outer 留下的做用域。當他執行 console.log(num++)
的時候,在他的做用域中沒有 num
變量他就會順着做用域鏈去尋找,最終在 outer 中的做用域中找到 num
並對其進行自加操做。因此當下次調用 getNum 的時候 num 會從 2 開始,不會是一開始的 1。
num 不是全局變量,還實現了 num 變量的重複調用。就達到了閉包的目的。
設置 num = 1
只是在 window
對象上添加存儲 num
的值,當下次調用 getNum
的時候 js 引擎還會從 getNum
做用域開始順着做用域鏈尋找 num
,在 outerAO 就會尋找到 num
,因此根本不會影響到 window 中的 num
,也不會受其影響。所以此段代碼輸出的結果爲 1 2 3
。
固然閉包也有其缺點
將引用內存函數對象的外部變量重置爲 null
getNum = null;
getNum 指向 outer 函數對象的那根線就會斷掉,三角關係破裂,那函數對象和 outerAO 也會相繼被銷燬。
以爲文章不錯的話還請各位大佬給個 star 鼓勵一下 gayhub