[ js 筆記 ] 閉包 Closure


Posted by Akira on 2022-03-11

MDN怎麼說?
閉包讓你可以在一個內層函數中訪問到其外層函數的作用域。在 JavaScript 中,每當創建一個函數,閉包就會在函數創建的同時被創建出來。

Scope

在了解閉包前,我們要先知道 Javascript 採用的是語彙範疇 ( Lexical Scope ) ,語彙範疇的特性是由所在的 Scope 往外層的 Scope 查找,一但找到就停止,或直到 Global 為止,且外層無法查找到內層。這個行為我們叫做「範圍鏈」(Scope Chain)。

function outer() {
    let name = "Chloe"
    function inner() { 
        console.log ('Hi,',name);
    }
    inner() 
}
outer(); // Hi, Chloe

outer() 中的函式 inner(),明明就沒有定義name的變數,卻可以打印出來,就是因為 name 去往外層的Scope去查找,在outer的 Scope 中找到了 name 的變數。如果你沒有學過其他語言,可能覺得是理所當然(我就是,一開始不懂為什麼要一直被拿出來強調...),後來才知道,在其他語言中,函式內的局部變數,只會在函式的執行期間存在。

總之要記住的就是:Javascript 會由內層往外層查找變數,找到就停止。反之則不行。

閉包

變數私有

為什麼要先了解 Scope 呢?我們換個方式再看一次剛剛的程式碼。

function outer(name) {
    return function inner() { 
        console.log ('Hi,',name);
    }
}
let innerFunc = outer('Chloe'); // Hi, Chloe
innerFunc()
  1. 建立一個函式 outer(),對其傳入參數 name,此參數只作用在 outer() 內,沒有任何參考指向他時,就會釋放記憶體。
  2. innerFunc() 雖然跳過 outer() 直接執行 inner() ,此時 name 需要一個參考值,所以往外層尋找,在 outer 找到了參數 name ,並在 innerFunc 時,參數的值只向 "Chloe" 。
  3. name 的變數持續被參考,因此會留在記憶體,此時 name 就變成 innerFunc 的私有變數,其他人無法修改也無法取得。

閉包是什麼?閉包就是⋯

當 inner 值回傳後,除了自己本身的程式碼,也能取得在 outer 的變數值,記住了在 outer 的執行環境,延長了 outer 中變數的作用時間,這就是閉包。而 innerFun 存取了 name的變數,讓變數私有。

閉包特性 - 封裝

再舉個應用方式 ↓

function add() {
 let start = 0
  return function(x) {
    return start += x 
  }
}

let addFunc = add();
addFunc(3) // 3
addFunc(3) // 6 
addFunc(3) // 9 

let addFunc2 = add();
addFunc2(3) // 3
addFunc2(4) // 7 
addFunc2(5) // 12

可以發現 addFunc 與 addFunc2 分別是兩個「獨立」的實體,這個概念是不是似曾相似呢?沒錯,閉包具有物件導向特性之一「封裝」的概念在。

參考資料:
重新認識 JavaScript: Day 19 閉包 Closure
MDN - 閉包


#javascript #closure







Related Posts

搶救茶壺大作戰:418 I am a teapot

搶救茶壺大作戰:418 I am a teapot

使用者行為分析&RFM分析

使用者行為分析&RFM分析

React Elements

React Elements


Comments