閉包是什麼
一個函式可以記住並存取它被宣告時所在的詞彙環境(Lexical environment)中的變數,即使外層函式已經執行完畢。
換句話說就是:閉包 = 函式 + 這個函式建立時所在的詞法環境。
詞法作用域 Lexical Scope
作用域在 【JavaScript 入門:作用域 Scope 和作用域鏈 Scope chain】文章中有介紹過,這邊簡單再提一下:作用域指的是「變數或函式可以被存取的範圍」。
在 JavaScript 中,作用域分成三種:
- 全域作用域(Global Scope)
- 函式作用域(Function Scope)
- 區塊作用域(Block Scope)
詞法作用域 又是甚麼? 詞法作用域也被稱為靜態作用域 (Static Scope),指的是:
變數能不能被存取」是在宣告時決定,而非執行時決定。
詞法作用域說明範例:
let name = "小明";
function sayHi(){
console.log(name);
}
function greeting(){
let name = "小美";
sayHi();
}
greeting();
這段程式的輸出會是什麼呢? 答案是 "小明"。因為詞法作用域的規則是:函式在哪定義,就會在哪找變數,跟執行的地方無關。
- 在定義階段,
sayHi在全域環境裡,同時這裡有一個let name = 小明,sayHi就記住了這個詞法環境。 - 執行
greeting()時,雖然greeting裡宣告了let name = 小美,但這是greeting內部的變數。 - 執行
sayHi時,函式會在被宣告的地方找變數,找到的 name 就是存著"小明" 的全域變數 。
這就是詞法作用域的概念,其實從這個例子和文章開頭的閉包簡介,應該可以稍微聯想到,我們甚至可以把閉包當作靜態作用域的產物。
閉包範例
直接以上面的程式碼來做修改:
function greeting(){
let name = "小美";
return function hey(){
console.log(name);
};
}
const sayHi = greeting();
sayHi();
這個例子中,我們用 sayHi() 來儲存 greeting() 的返回值 hey(),當執行 sayHi()時,雖然 greeting() 已經結束,但是回傳的函式記住了被定義時外層的 let name = "小美",因此執行時可以印出 "小美"。
再看一個複雜一點的範例,但概念都是一樣的。下方以一個密碼管理的程式為範例;
function passwordManager(initialPassword){
let password = initialPassword; // 將傳入的參數作為初始的密碼
return{ // 返回一個包含兩個函式的物件
setPassword:function(newPassword){
password = newPassword;
console.log("密碼設定成功!");
},
checkPassword:function(input){
let res = (input === password)?"正確":"錯誤";
console.log(`密碼${res}`);
}
};
}
const passwordManagerInstance = passwordManager(1234); // 初始化一個密碼管理器,並設定初始密碼為 1234
passwordManagerInstance .checkPassword(2345); // 使用回傳的函式檢查密碼,會輸出"密碼錯誤"
passwordManagerInstance .setPassword(2345); // 修改密碼
passwordManagerInstance .checkPassword(2345); // 再次使用回傳的函式檢查密碼,會輸出"密碼正確"
passwordManager()被執行passwordManager()回傳了包含setPassword()函式與checkPassword()函式的物件passwordManager()執行結束- 但
setPassword()與checkPassword()記住了被宣告時外層的變數password password被「關在」setPassword()與checkPassword()的共同詞法環境裡 → 這就是閉包
📌 重點:
只要詞法環境仍被函式引用,該環境就不會被Javascript 的回收機制收掉,被閉包關注的變數就能繼續存活。所以
password沒有消失,是因為還有人(setPassword/checkPassword)在使用。
總結
這篇文章中簡單介紹了閉包的概念,也給了幾個範例,最後用一句話總結閉包:
函式 + 這個函式建立時所在的詞法環境。這讓內部函式即使在外部函式結束後,仍可以存取並記住定義在外層的變數。










