iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 19
1
Modern Web

【這些年我似是非懂的 Javascript】系列 第 19

# 【這些年我似是非懂的 Javascript】Day 19 - 閉包 (Closure) # Part 1

經過瞭解語彙範疇後,今天要來分享 JS 我好像似懂又非懂的"閉包 (Closure)"。

什麼是閉包 ?

  • 我之前的想法 :
    Function 包 Function 就是閉包。
  • 實際上:
    其實是一個函式並可以記住並且可以存取語彙範疇,並且當函式在他的語彙範疇外執行時也可以的功能。

所以我之前的想法其實不算是非常正確的,
原因等等看一看就會明白了。

那我們平常在哪裡可以看得到?

真的沒有在唬爛,只要你有心人人都是閉包。

說這麼多先說說他的好處是什麼?
他可以不需要使用到全域變數,
在不污染環境的情況下讓外部可以修改該狀態。

有點像是你把它封裝好的包裹,然後你可以透過念能力在外部可以使用。 (最近獵人看太多xD )

聽不懂沒關係我們直接看看範例,

function foo(){
    var a = '17';
    
    function bar(){
        console.log(a);
    }
    bar(); // '17'
}

還記得之前學的嗎?
語彙範疇的查找功能 RHS,所以 boo 查找 a 會找到外層 foo 宣告的 a

所以他... 是閉包嗎?
如果依照我之前以為的

閉包就是 Function 包 Function

那他就是閉包,因為他的確是 Function 包 Function。

但是你反觀剛剛定義的閉包,

其實是一個函式並可以記住並且可以存取語彙範疇,並且當該函式在他的語彙範疇外執行時也可以的功能。

那看起來就不完全不是了啊,

到目前為止好像還是看不出來閉包是什麼...
那我們再改一下上面的範例

function foo(){
    var a = '17';
    
    function bar(){
        console.log(a);
    }
    return bar; // 注意這邊沒有執行 bar 本身
}

var baz = foo();

baz(); // 2 看到了閉包的蹤影

你會發現 baz 其實執行的就是 foo Function 內層的 bar 對吧!?
而且還可以讀取到原本在該語彙範疇所宣告的 a
那就達成了剛剛所說的。

一個函式並可以記住並且可以存取語彙範疇,並且當該函式在他的語彙範疇外執行時也可以的功能。

等等等...
之前不是有提過有垃圾收集器 (Garbage Collection),那這樣看 foo 在執行後理論上裡面的記憶體應該會被回收才是啊,
那為何在閉包中不適用呢?
原因是閉包中的內層 bar() 函式本身還是有一個參考指向到讓外層的 foo() 讓這範疇可以繼續留存,而那個參考就叫做閉包。

迴圈與閉包

之前我其實踩過這個坑就是我想要用一個 for 迴圈並且加上 setTiimeout 每秒執行一次迴圈就好,
實際寫出來長這樣。

for(var i=0; i<=5 ; i++){
    setTimeout(function timer(){
        console.log(i);
    },1000)
}

理論上看起來會是一秒執行一次對吧!?
第一圈等待一秒再接著下一圈,
但事實是...

6
6
6
6
6

Why?
看結果而言看起來就是他一路向西走,直接把迴圈執行到底然後統一都在一秒後一次 console 出來,6的由來是因為最後一次加完他 >=5 所以不執行第六次,所以才會拿到五次的 6
(這部分牽扯到 JS 的非同步,之後有機會再說)

所以我到底該如何實作這個?

for (var i = 1; i <= 5; i++) {
  (function(j) {
    setTimeout(function timer() {
      console.log(j);
    }, j * 1000);
  })(i);
}

Nice ~ 透過閉包並且還應用到之前提的 IIFE 每次迭代來建立一個新的範疇...
每次迭代來建立一個新的範疇...
每次迭代來建立...
每次迭代...
那其實可以直接用 let 搞定對吧!?
我們只需要建立一個區塊範疇就搞定啦~

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

是不是簡單多了!?


今天先分享到這,明天來講講剩餘的一部分,
邊旅遊還要空出時間寫文章真的不容易xDD
連假是魔鬼QQ
先這樣囉~

感謝各位

參考來源:

你所不知道的 JS|範疇與 Closures,this 與物件原型 (You Don't Know JS: this & Object Prototypes))


上一篇
【這些年我似是非懂的 Javascript】Day 18 - 拉升(Hoisting)
下一篇
【這些年我似是非懂的 Javascript】Day 20 - 閉包 (Closure) # Part 2
系列文
【這些年我似是非懂的 Javascript】32

尚未有邦友留言

立即登入留言