閉包


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

[筆記] React 隨手記 (React 抽獎機率設定)

[筆記] React 隨手記 (React 抽獎機率設定)

〈 演算法與資料結構 #1〉Recursion 遞迴函式

〈 演算法與資料結構 #1〉Recursion 遞迴函式

D47_W6HW1

D47_W6HW1


Comments