iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 18
0
Modern Web

JavaScript Note系列 第 18

Closure 閉包

閉包(Closure )在JavaScript中是一個比較深入的議題,想了解閉包的原理,得先從幾個觀念著手:

  • 全域、區域執行環境
  • 作用域鏈(scope chain)
  • 呼叫堆疊(call stack)

還未熟悉這些觀念的讀者,可以先從這方面著手,再來了解閉包。

到底什麼是閉包?說實話,還真是不好解釋,所以先來看範例,再從中了解。

var globalValue = 'Hello!';
function outerFun() {
    console.log(globalValue);
}
outerFun(); //Hello!

很簡單的範例,結果不是重點,重點是一堆人寫過這樣的程式,卻不知道建立一個閉包。
變數globalValue與函式outerFun( )都宣告在全域執行環境(閉包)。
在這裡,閉包所代表的是全域執行環境。閉包內,outerFun( )可以存取globalValue。

再看下一個。

var globalValue = 'Hello!';
var outer;
function outerFun() {
    var localValue = 'Mary';
    function innerFun() {
        console.log(`${globalValue} ${localValue}`);
    }
    outer = innerFun;
}
outerFun();
outer();

我們來分析會如何執行:

  • outerFun( )沒問題,可以正常執行。
  • 在outerFun( )內部,把innerFun( )函式參考複製給全域變數outer。
  • 當執行outer( )時,outerFun( )的執行環境已經移除了,連帶內部的變數localValue一併消失。
  • 所以可以預期的,無法找到localValue。

但結果:
https://ithelp.ithome.com.tw/upload/images/20181102/20112573ItP4spk9W9.png
咦!localValue還活著,明明outerFun( )的執行環境已經消失了,為何localValue沒事?

是因為閉包。
當我們在outerFun( )內建立innerFun( ),其實就是在建立閉包。

閉包內部包含了函式在定義時,它的作用域範圍能夠存取的變數,所以即便outerFun( )的執行環境消失了,閉包依舊將此變數保存於記憶體中,讓作用域範圍內的函式能夠看到它。

我們再看另一個例子。

function useCounter() {
    var times = 0;
    function counter() {
        times++;
        return times;
    }
    return counter;
}
var runCounter = useCounter();
console.log(runCounter()); //1
console.log(runCounter()); //2
console.log(runCounter()); //3

跟剛剛的範例差不多,只不過結果是回傳函式。

  • 建立的閉包,包含counter( )函式,以及它作用域範圍內的變數times。
  • 當useCounter( )回傳counter( )的函式參考給變數runCounter後,它的執行環境就會消失。
  • 此時,我們執行runCounter( ),依然可藉由閉包,存取變數times,並在counter( )內部做完遞增後回傳。

總結:
閉包讓函式可以存取函式在定義(非執行)時,其作用域範圍內的變數,它為函式及其所屬的變數架起安全網,如此一來,即使函式所屬的執行環境消失了,它仍然可以存取閉包內的變數。

參考來源:
忍者:JavaScript開發技巧探秘 第五章


上一篇
This
下一篇
call函式 & arguments物件
系列文
JavaScript Note31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言