JavaScript-閉包(Closures)

什麼是閉包?

閉包是一種資料結構,包含函式以及該函式被宣告時所建立的環境組合,因此當函式是在其宣告的語彙範疇之外執行時也能正常運作。

在說閉包以前先來複習範圍練

function outside() {
  let a = 1;
  function inside() {
    console.log(a);
  }
  inside();
}
outside(); //1

inside()這個函示裡是沒有 a 變數,因為沒有他會向外尋找,在上一層找到 a 變數就停止,假設在上一層也沒有就會再往外一層去做尋找,直到在全域裡也找不到,就會回傳 not defined。

有使用閉包與沒使用閉包的差別

  1. 沒使用閉包
    var count = 0;
    function number() {
      return ++count;
    }
    console.log(number());
    console.log(number());
    console.log(number());
    

    當我想要累加時會用一個全域變數來儲存,但當時用 var 就有可能會影響到其他程式碼,並造成錯誤。 如果我用 let,因為 let 是 block scope{},出去{}後就會消失數字也不會累加上去,永遠都會是1。

  2. 有使用閉包
    function number() {
      let count = 0;
      function times() {
        return ++count;
      }
      return times;
    }
    let witch = number();
    let witch2 = number();
    
    console.log(witch()); //1
    console.log(witch()); //2
    console.log(witch()); //3
    console.log(witch2()); //1
    console.log(witch2()); //2
    

    像這樣使用閉包就算不用 let 宣告變數,也不用擔心 var 變數會污染到,不是這個函式的其他程式碼,因為範圍練不在上一層找不到,所以不會影響。而且 let 變數也因為閉包而不會消失,而是可以繼續累加,使用閉包還可以使不同物件的累加不會相互影響。

    function number() {
      let count = 0;
      return () => ++count;
    }
    let witch = number();
    let witch2 = number();
    
    console.log(witch()); //1
    console.log(witch()); //2
    console.log(witch()); //3
    console.log(witch2()); //1
    console.log(witch2()); //2
    

    還可以搭配箭頭函示使程式碼更簡短。

setTimeout()產生的閉包

同樣的函式letvar的會產生不同的結果

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); // 3,3,3
  }, i * 1000);
}

因為 var 是全域變數,是屬於 function socpe,這個變數會一直在,所以不會產生閉包,要等 stack 清空排隊的資料才會上去,等到時候 i 已變成 3。

for (let i = 0; i < 3; i++) {
  //Closure 閉包
  setTimeout(() => {
    console.log(i); //0,1,2
  }, i * 1000);
}

因為 i 是用 let 宣告是屬於 block socpe 所以如果等到迴圈跑完,我 i 都不見了,為了防止 i 不見會把 i 包起來一起跑到 web apis 排隊印出。

comments powered by Disqus