前端三十|09. [JS] 什麼是閉包?
Published in
8 min readSep 25, 2019
昨天我們聊到到了 JavaScript 中的事件迴圈,文中末段提到了透過 IIFE 的解決方法:
for (var i = 1; i <= 5; i++) {
(function (x) {
setTimeout(function () {
console.log(x)
}, 1000 * x)
})(i)
}
仔細想想蠻奇怪的對吧?原本的版本中,console.log()
都指到同一個變數 i
,為什麼在經過一層函式後,當下 i
的數值就能被保留住呢?
這就關係到今天的主題 — 閉包(Closure)。
本系列文已經重新編校彙整編輯成冊,並正式出版囉!
《前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法》好評販售中!
喜歡我文章內容的讀者們,歡迎您前往購買支持!
Closure
閉包這個名詞,對稍有經驗的開發者應該都不陌生,但具體來說是指什麼呢?
一如既往的,讓我們從範例程式出發:
function add(num) {
function func(x = 0) {
return num + x
}
return func
}
let addFive = add(5)
console.log(addFive(8)) // 8 + 5 = 13
add
是一個接收參數 num
、回傳函式 func
的函式。由於 JavaScript 有自動回收機制,理論上在函式執行完畢後會將函式所佔用的記憶體空間釋放;但在此處的範例中,可以看到參數 num
在 add
執行完畢後,仍然可以被回傳出來的函式使用,沒有跟著 add 一起被回收掉。
這種把外層變數包在內層使用的方法,也就是耳熟能詳的閉包。
初學者可能會對「回傳函式的函式」感到有些困惑,但從 JavaScript 的基本型別來看是非常正常的事情;更詳細的說明會在後續系列文中提到,這邊暫且先大概知道就好囉。
讓我們再看一個稍微複雜的例子:
loadPicture() {
let count = this.pageContent.length
const load = (target, resolve) => {
const counter = () => (--count ? count : resolve())
let img = new Image()
img.onload = counter
img.onerror = counter…