閉包


Posted by wayne201299 on 2023-09-21

之前完全沒使用過閉包的技巧,身為一個前端工程師,寫了這麼多年的code,到底什麼是閉包?就用這篇來記錄自己對於閉包這個名詞的解釋吧!主要會以我在網路上蒐集到關於閉包的文章加上自己的見解來寫,盡量白話一點解釋

閉包(Closure)是函式以及該函式被宣告時所在的作用域環境(lexical environment)的組合,也就是說閉包可以讓你透過內層的方法取得外部的作用域,在JavaScript中每次方法建立時就會建立一個閉包

重新解釋一遍

當一個function被建立時,會儲存一組當下的作用域及function本身在記憶體中

作用域:指的是程式執行時當下各個變數值有效的範圍

情境

想想執行下面這段程式碼會發生什麼事?

function makeFunc(){
    var name = 'Mozilla'; // name為存在makeFunc中的區域變數
    function closureFunc(){ // closureFunc為內部函式,是一個閉包
        alert(name); // 使用外部函式宣告的變數
    }
    closureFunc();
}
makeFunc()
  1. 宣告了一個 makeFunc 的函式
  2. makeFunc內包含了nameclosureFunc()
  3. 內部的closureFunc()透過取得外部函式makeFunc()來取得name變數值

結論:
在JavaScript執行環境中,內部函式是可以訪問到外部函式的變數值的

閉包

換舉另一個例子

function makeFunc() {
  var name = "Mozilla";
  function closureFunc() {
    alert(name);
  }
  return closureFunc;
}
var myFunc = makeFunc(); // myFunc會是一個closureFunc
myFunc();  // 單獨執行closureFunc
  • 預期執行myFunc()時,name會是undefined,結果卻可以正常吃到name! 🤨

閉包為函式的組合、還有宣告該函式的作用域環境。這個環境包含閉包建立時,所有位於該作用域的區域變數

  • 變數myFunc會作為一個參照,裡面包含了closureFunc()name當下的value
  • myFunc()再次執行時,會從記憶體位址中取得closureFunc()及當下作用域的變數值,所以alert會有結果

再舉一個例子

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12
  1. 宣告一個add5 作用域,x=5
  2. 宣告一個add10 作用域,x=10
  3. 執行add5(2),此時add5作用域內已經有x=5了,傳入y=2,x+y=7
  4. 執行add10(2)

函式執行時當下的作用域非常重要!!!

使用

這邊有幾個主要的使用場景

  • HTML 上的event 觸發,回傳callback時
  • 模擬private 方法,保護作用域內的變數命名不會溢出,Module pattern

      var counter = (function() {
        var privateCounter = 0;
        function changeBy(val) {
          privateCounter += val;
        }
        return {
          increment: function() {
            changeBy(1);
          },
          decrement: function() {
            changeBy(-1);
          },
          value: function() {
            return privateCounter;
          }
        };
      })();
      console.log(counter.value()); // logs 0
      counter.increment();
      counter.increment();
      console.log(counter.value()); // logs 2
      counter.decrement();
      console.log(counter.value()); // logs 1
    

    在這邊我們建立一個counter環境,被incrementdecrementvalue所共用,透過IIFE(立即呼叫函式),建立後立刻執行,裡面包含了privateCounterchangeBy()兩個private物件,這兩個物件只能在counter內部中使用

Module Pattern(Reveal Module):
建立一個module,而內層的函式具有閉包的特性,因此可以存取外部的物件及方法,但同時不會洩漏內部資訊,還可以控制要回傳的內容


#javascript







Related Posts

Sequence to Sequence

Sequence to Sequence

The introduction and difference between class component and function component in React

The introduction and difference between class component and function component in React

JavaScript 核心觀念 - 屬性特徵是什麼?

JavaScript 核心觀念 - 屬性特徵是什麼?


Comments