什麼是 IIFE?

當迴圈遇到setTimeout()

假設想透過迴圈 + setTimeout() 來做到,在五秒鐘之內,每秒鐘依序透過 console.log 印出: 0 1 2 3 4

我們會直覺的寫下

1
2
3
4
5
for( var i = 0; i < 5; i++ ) {
window.setTimeout(function() {
console.log(i);
}, 1000);
}

印出來的結果是5個5。

為什麼不是1,2,3,4,5呢???

因為

JavaScript 是一個「非同步」的語言,所以當我們執行這段程式時, for迴圈並不會等待window.setTimeout()結束後才繼續。

而是在執行階段就一口氣就把他跑完。

所以for迴圈在開始執行的第一個瞬間(0的時候),就把五次的window.setTimeout()跑完。然後跑到1時,全部把5印出來。

要如何避免這個問題呢?

可以利用「切分變數有效範圍的最小單位是function」這個特性,把window.setTimeout()透過一個「立即被呼叫的特殊函式」來包裝。

像是這樣寫

1
2
3
(function(){
// 做某事...
})();

下面來解釋這個這個神奇的寫法

  1. 先加個小括號()把這個函式包起來
1
2
3
(function doSomething ( i ){
// 做某事...
})
  1. 因為要呼叫它,所以在後面再加個小括號 ()
1
2
3
(function doSomething ( i ){
// 做某事...
})(123)
  1. 比對一下

一般呼叫函式

1
doSomething(123);

函式宣告當下即呼叫

1
2
3
(function doSomething ( i ){
// 做某事...
})(123);

可以很清楚的了解,立即函數,就如其名,它要馬上被執行。

解決window.setTimeout問題

回到for迴圈與window.setTimeout的問題。

1
2
3
4
5
for( var i = 0; i < 5; i++ ) {
window.setTimeout(function() {
console.log(i);
}, 1000);
}

我們知道像上面這樣的寫法,在執行window.setTimeout的時候, i早已變成了5。

那麼為了可以保留每一次執行迴圈時,那個「當下的」i,我們可以用一個立即被呼叫的特殊函式將它包覆起來,然後將i作為參數傳入:

1
2
3
4
5
6
7
for( var i = 0; i < 5; i++ ) {
(function(i){
window.setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}

印出的結果就會是我們要的1,2,3,4,5

封裝

廣義來說,IIFE也是儲存閉包的環境狀態的作法,在執行 setTimeout()的同時,會將當下的 i 鎖起來,延長它的生命。

Alex宅幹嘛的直播裡,31.28s 有提到封裝的概念,寫Jquery第一件事情先處理scope,不處理的話name會被註冊在window上面,任何人都可以去讀取,使用,覆蓋我寫的資料。

以一般來說

1
2
3
4
<script>
//scope
var name = "Phoebe";
</script>

在開發人員工具可以直接讀取到name

而scope就是開一個密閉的空間,寫在裡面,不給別人看到。

1
2
3
4
5
6
//scope
//IIFES
(function () {
var Myname = "Phoebe";
})();

使用立即函式封裝

封裝後就讀取不到了

參考文章

本筆記參考以下文章加上自己所理解出來的內容撰寫。

重新認識 JavaScript: Day 18 Callback Function 與 IIFE


什麼是 IIFE?
https://phoebeho.com/Javascript/20210124/2511710637/
作者
Phoebe
發布於
2021年1月24日
許可協議