前端三十|09. [JS] 什麼是閉包?

Schaos
Schaos’s Blog
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 有自動回收機制,理論上在函式執行完畢後會將函式所佔用的記憶體空間釋放;但在此處的範例中,可以看到參數 numadd 執行完畢後,仍然可以被回傳出來的函式使用,沒有跟著 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

--

--