本文主要和大家詳細介紹JavaScript閉包,說起閉包,相信寫前端的同學都知道,而且相信在實際的專案中或多或少都已經用到了閉包。那到底什麼才是閉包,閉包又是怎麼產生的呢?
1. 什麼是閉包
在阮老師的文章中提到:
閉包就是能夠讀取其他函數內部變數的函數。由於在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成"定義在一個函數內部的函數"。
2. 閉包的作用
一個是可以讀取函數內部的變量,另一個就是讓這些變數的值始終保持在記憶體中。
3. 一個簡單的閉包實例
#function count() { let num = 0; return function add() { return ++num; } }let a = count(); a(); //1a(); //2
先將count()的回傳結果也就是count()函數裡的回傳的函數賦值給a。此時,count()中定義的局部變數num就保存在記憶體中。當第一次呼叫a()時,回傳++num,即1;當第二次呼叫a()時,由於此時num是1,所以回傳的結果是2
4. 產生閉包的原因
相信很多人對這個問題都很困惑,都會認為一個函數中,回傳一個函數,這樣就形成了閉包。其實這只是閉包產生的方法,而不是原因,以下將為大家解釋原因。
產生的主要原因是因為JavaScript是詞法作用域的,也就是在該函數定義#時就已經被賦予了一個作用域。然後在運行時,又會根據實際運行情況被賦予運行時的作用域。透過這兩個作用域一個JS函數才會被正確執行。
以上例為例,在執行count()的時候,函數的作用域是
#運行時作用域num = 0 |
---|
#詞法作用域 |
當count()執行時,傳回add函數的時候,由於此時add處於定義狀態,故返回時產生的該函數的詞法作用域即為上述count()的作用域。所以執行a()的時候,其真正的作用域是
add運行時的作用域 |
---|
count運行時作用域num = 0 |
count詞法作用域 |
所以第一次呼叫add時,由於num是0,所以回傳1;而第二次回傳是2。
5. 總結
從上述的描述中,可以看出由於生成閉包的時候,外部函數的局部變數(運行時作用域)被內部函數作為詞法作用域保存在記憶體中,故當內部函數被釋放之前,該區塊記憶體是不會被釋放的。因此在使用閉包時,需要非常注意記憶體洩漏的問題。