今天來點 Scope Chain (作用域鏈) 與 Closure (閉包)!
Scope Chain(作用域鏈)是什麼?
當 JavaScript 使用每一個變數時,會先嘗試在目前的作用域中尋找該變數;
若找不到該變數,便會一直往外層作用域尋找,直到全域作用域還是沒找到的話就會直接報錯。
這一層一層的關係,就是作用域鏈。
以下是一段簡單的範例:
1 | let a = 100; |
Closure(閉包)是什麼?
閉包(Closure)是 JavaScript 中的一個重要概念,每當創建一個函式時,都會同時創建一個閉包。
閉包(Closure)是一個函式和此函式被宣告時所在的詞法環境組合而成的。
詞法環境(Lexical Environment)是 JavaScript 用於管理變數、函數和作用域。當函數被創建時,同時也會創建一個詞法環境,這個環境記錄了函數內部的變數、函數聲明以及對外部作用域的引用。
來看一個簡單的例子:
1 | function say(greeting) { |
大家是否還記得上篇提及的 Execution Context,當 JavaScript 執行以上程式碼時:
開始執行 Global Execution Context,因為 JavaScript 中的 hoisting(提升),function say 已經被建立且儲存在記憶體中,並且可以在作用域中使用。
Global Execution Context 繼續執行
var host = say('Welcome');
。在執行say('Welcome')
時,一個新的 Execution Context 被建立,greeting
也被儲存在 function say 的 Execution Context 中。 透過 say 函式,內部建立一個匿名函式。此時,function say 也就執行完了。雖然 Execution Context 已經不在了,但其中的變數還是儲存在那個記憶體位置!結束前一個任務後,繼續回到 Global Execution Context,接著,碰到了
host('Viii')
於是我們建立了一個給匿名函式的 Execution Context,同時裡面帶有參數 name,因為此時在自己的 function 裡面找不到greeting
這個變數,所以會開始透過 Scope Chain 尋找。因為前面有提及 function say 在記憶體位置仍留有參照,所以在 function say 裡面所建立的函式仍然可以找得到greeting
這個變數。最後就印出結果Welcome Viii
。
Brief Summary
可以在第三點看出,匿名函示式的 Execution Context 與外面的變數 greeting
close 在一起了,這就是閉包 (Closure)。透過這樣的特性,可以確保當在執行 function 的時候,JavaScript 能夠找到其相對應的變數。
透過閉包讓 function 擁有 private 變數
理解閉包的概念後,可以透過閉包讓 function 擁有 private 變數,
在這個例子中,chocolateCount 變數被保護在 chocolateBox 函式的作用域內,
只能通過 eatChocolate 函式來訪問和修改。
外部的程式碼無法直接訪問或修改 chocolateCount 變數。
1 | function chocolateBox() { |
確保了私有變數在不同的執行環境中是獨立的
閉包的一個重要特性,確保私有變數在不同的執行環境中是獨立的。
創建出兩個不同的 box1 和 box2,並且都是使用 chocolateBox 函式創建。
即使使用了相同的函式,但每個 box 都有自己獨立的 chocolateCount 變數,
因此彼此之間不會互相影響,每個盒子都可以獨立地操作巧克力數量。
1 | function chocolateBox() { |
今天簡單地講述作用域鏈與閉包,下篇會繼續補充閉包的應用以及相關重要小知識!下篇待續!
(持續理解所有的為什麼! go! go! go!)ヽ(●>∀<●)ノ
參考資料:
➫ Javascript 的作用域 (Scope) 與作用域鏈 (Scope Chain) 是什麼?
➫ MDN - Closures
➫ [筆記]-JavaScript 閉包(Closure)是什麼?關於閉包的3件事
➫ [筆記] 談談JavaScript中closure的概念 – Part 1