JavaScript 中的閉包

JavaScript 中的閉包

本文將解釋 JavaScript 中的閉包。

YouTube Video

JavaScript 中的閉包

在 JavaScript 中,閉包是一個非常重要且強大的概念。通過理解閉包,您可以掌握在許多情況下有用的知識,例如函數的行為和範圍,以及 JavaScript 的異步處理和事件處理。在這裡,我們將詳細解釋從閉包的基本定義到具體範例及其應用的一切內容。

什麼是閉包?

閉包是指函數即使在其創建範圍之外被調用,仍然能訪問創建範圍內變量的機制。利用閉包,函數可以不斷“記住”外部變量。

閉包由以下兩個元素組成。

  1. 函數定義(函數本身)

  2. 函數定義的作用域(函數本體之外的變量和其他函數)

在 JavaScript 中,閉包之所以可能,是因為函數具有訪問其創建範圍內的變量的能力。

基本範例

首先,讓我們來看看一個閉包的基本範例。在以下代碼中,outerFunction 返回了一個名為 innerFunction 的函數。重要的一點是,innerFunction 能夠訪問在 outerFunction 的作用域中定義的變量 count

 1function outerFunction() {
 2    let count = 0;
 3
 4    function innerFunction() {
 5        count++;
 6        console.log(`Current count: ${count}`);
 7    }
 8
 9    return innerFunction;
10}
11
12const counter = outerFunction();
13counter(); // Current count: 1
14counter(); // Current count: 2
15counter(); // Current count: 3

閉包的運作原理

如上述範例所示,即使在 outerFunction 執行完畢後,count 仍然由 innerFunction 保留。innerFunction 可以繼續訪問 outerFunction 的作用域,因此 count 可以在 innerFunction 中更新。這是閉包的基本機制。

innerFunction 被賦值給變量 counter,我們可以看到即使 outerFunction 已執行完畢,count 的狀態仍然被保留。這是因為 JavaScript 持續“記住”函數定義時的作用域。

應用:將閉包用作私有變量

閉包可以像面向對象編程中的「私有變量」一樣使用。通常在 JavaScript 中,對象屬性可以從外部直接訪問,但通過使用閉包,可以防止外部直接操作函數作用域內的變量。

在下一個示例中,createCounter 函數使用閉包創建了一個計數器,並返回一個帶有私有變量 count 的計數器。

 1function createCounter() {
 2    let count = 0;
 3
 4    return {
 5        increment: function() {
 6            count++;
 7            console.log(`Count: ${count}`);
 8        },
 9        decrement: function() {
10            count--;
11            console.log(`Count: ${count}`);
12        },
13        getCount: function() {
14            return count;
15        }
16    };
17}
18
19const myCounter = createCounter();
20myCounter.increment(); // Count: 1
21myCounter.increment(); // Count: 2
22myCounter.decrement(); // Count: 1
23console.log(myCounter.getCount()); // 1

在此示例中,count 位於 createCounter 函數的作用域內,因此無法從外部直接訪問。然而,可以通過 incrementdecrement 方法操作它。通過這種方式,使用閉包可以在 JavaScript 中實現私有變量的概念。

閉包的實用示例

與回調函數的結合

閉包通常與回調函數結合使用來管理異步處理。例如,讓我們考慮一個使用定時器的示例。

 1function startTimer(duration) {
 2    let timeLeft = duration;
 3
 4    function countdown() {
 5        console.log(`Time left: ${timeLeft} seconds`);
 6        timeLeft--;
 7
 8        if (timeLeft >= 0) {
 9            setTimeout(countdown, 1000);
10        }
11    }
12
13    countdown();
14}
15
16startTimer(5);
17// Time left: 5 seconds
18// Time left: 4 seconds
19// Time left: 3 seconds
20// Time left: 2 seconds
21// Time left: 1 second
22// Time left: 0 seconds

在此示例中,countdown 函數訪問了 startTimer 作用域內的 timeLeft 變量。通過這種方式,閉包對於像定時器這樣的異步處理非常有用,因為它們能夠隨時間維持變量的狀態。

事件處理程序

閉包在設置事件處理程序時也非常方便。在下面的示例中,使用閉包記錄按鈕被點擊的次數。

 1function setupClickCounter(buttonId) {
 2    let clickCount = 0;
 3
 4    const button = document.getElementById(buttonId);
 5    button.addEventListener('click', function() {
 6        clickCount++;
 7        console.log(`Button clicked ${clickCount} times`);
 8    });
 9}
10
11setupClickCounter('myButton');

在這種情況下,每次點擊時 clickCount 都會增加,並且其值會被保留。通過使用閉包,可以為每個按鈕分配一個獨立的計數器。

結論

閉包是一個象徵 JavaScript 靈活性和強大功能的概念。它們將變量封閉在函數作用域內,並通過外部可訪問的函數對這些變量進行操作。通過理解並利用這一機制,可以在 JavaScript 編程中掌握更高級的技術。

閉包應用於各種情況,從事件處理和異步處理到面向對象編程的偽實現。

您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。

YouTube Video