# JavaScript經典考題 - setTimeout(Event Loop) & Scope feat.IIFE
## 1. 試問下列輸出為何?
```javascript=
for(var i = 1; i <= 5; i++) {
setTimeout(function() { console.log(i) }, 0)
}
```
### 答案:
> 6
> 6
> 6
> 6
> 6
### 解說:
1. **setTimeout 結構:setTimeout( callback function, [ 時間 ], [ 參數1, 參數2, ... ] )**
:::warning
[ 時間 ] 為Optional,決定執行setTimeout之前的等待時間,default 為 0
:::
:::warning
[ 參數1, 參數2, ... ] 為Optional,決定帶入 callback function 的參數為何
:::
:::warning
setTimeout 為 瀏覽器 提供的 WebAPI ( ex: document、XMLHttpRequest )
:::
:::warning
return:回傳代表此setTimeout的ID(Number),可用在clearTimeout()
:::
2. setTimeout 在 **Event Loop** 裡屬於 queues 非同步事件( ex.click、setTimeout、ajax )
- 執行時會先等"時間"倒數完後
- 才把"callback function"丟到 **Callback Queue / Task Queue**
- 必須等到 **Call Stack** 所有函式執行完畢
- 才會把"callback function"丟回 **Call Stack**

>https://prashantb.me/javascript-call-stack-event-loop-and-callbacks/
:::danger
Heap:在程式中宣告、定義變數、函式…等的記憶體位置
:::
>[Event Loop 演示工具](http://latentflip.com/loupe/?code=Zm9yKGxldCBpID0gMTsgaSA8PSA1OyBpKyspIHsNCiAgc2V0VGltZW91dChmdW5jdGlvbigpIHsgY29uc29sZS5sb2coaSl9LDUwMDApDQp9!!!)
3. 在此 for迴圈 為
```javascript
for(var i = 1; i <= 5; i++) { ... }
```
- var 作用域為 ==function scope==,當 i 再被賦值的時候,不會被綁在for的{...}裡,也表示 var i 在此不會留下作用域
4. 在此 callback function 為
```javascript
function() { console.log(i) }
```
- 當被丟回 Call Stack 時,由於<font color="red">var i 在此迴圈不會留下作用域</font>,for迴圈已經跑完,i=6
- 此時console.log(i)應該輸出 **<font color="green">"執行時當下的i",也就是迴圈執行完後i=6</font>**
:::warning
簡單來說, console.log(i); 的 i 變數會去這個 callback function 的"外層"做存取,而 for迴圈 並不會等待每個 setTimeout 結束才繼續,當 callback function 執行時,拿到的 i 是跑完 for迴圈 的 6
:::
>[重新認識 JavaScript: Day 18 Callback Function 與 IIFE](https://ithelp.ithome.com.tw/articles/10192739)
## 2. 試問下列輸出為何?
```javascript=
for(let i = 1; i <= 5; i++) {
setTimeout(function() { console.log(i) }, 0)
}
```
### 答案:
> 1
> 2
> 3
> 4
> 5
### 解說:
1. 在此 for迴圈 為
```javascript
for(let i = 1; i <= 5; i++) { ... }
```
- let 作用域為 ==block scope==,當 i 再被賦值的時候,會被綁在for的{...}裡,此時每次迴圈都會留下"塊作用域"
2. 在此 callback function 為
```javascript
function() { console.log(i) }
```
- 當被丟回 Call Stack 時,因為<font color="red">let i每次迴圈都會留下"塊作用域"</font>
- 此時console.log(i)應該輸出 **<font color="green">"每次迴圈所綁定作用域的i"</font>**
## 3. 試問下列輸出為何?
```javascript=
for(var i = 1; i <= 5; i++) {
setTimeout( function(x){ console.log(x) }, 0, i)
}
```
### 答案:
> 1
> 2
> 3
> 4
> 5
### 解說:
3. 在此 setTimeout 為
```javascript
setTimeout( function(x){ console.log(x) }, 0, i)
```
- 每當執行 callback function 時,<font color="red">帶入 i 給參數 x</font>
4. 在此 for迴圈 為
```javascript
for(var i = 1; i <= 5; i++) {...}
```
- 迴圈每次執行所帶入 callback function 的參數 x,因為 **<font color="green">" i 被賦予的值不同"</font>** 而有所不同
## 4. 試問下列輸出為何?
```javascript=
for(var i = 1; i <= 5; i++) {
(function (x) {
setTimeout(function() { console.log(x) }, 0)
})(i)
}
```
### 答案:
> 1
> 2
> 3
> 4
> 5
### 解說:
1. 在此使用了 IIFE 立即函式
```javascript
(function (x) {
setTimeout(function() { console.log(x) }, 0)
})(i)
```
:::warning
**IIFE** (Immediately Invoked Function Expression,立即函式) ,是一個定義完"立即"執行的 JavaScript function
<br>
**結構**:
大致分為兩部分
##### 1. 使用 "( )"分組運算子 包起來的 anonymous function(匿名函式)
###### - 使外層無法訪問function內的變數,以避免裡面的變數污染到 global scope (減少"全域變數"的產生 與 避免變數名稱的衝突)
###### - 若立即執行之後有再呼叫此函式的需求,也可以使用函式宣告賦予function名稱
##### 2. 在(function([參數])(...))後面使用function expression 的 "([呼叫時欲帶入的參數])"
###### - 以立即 Invoke(呼叫/調用) 此函式
```javascript
(function(){
//doSomething...
})();
//or
(function(參數名稱){
//doSomething...
})(參數值);
//or
(function 函式名稱 (參數名稱){
//doSomething...
})(參數值);
```
:::
2. 在此 console.log(i); 所被帶入的 i 將會是每次迴圈被賦予不同值的i
:::warning
也就是每次迴圈裡,函式被立即呼叫時所代入的 **<font color="green">"每次回迴圈當下的 i "</font>**,而 i 被當作參數傳入function(x){...},而留有作用域
:::
## 5. 試問下列輸出為何?
```javascript=
let i = 0;
while(i++ <5) {
setTimeout(()=> { console.log(i) }, i*1000);
}
```
### 答案:
> 5
> 5
> 5
> 5
> 5
### 如何達到輸出秒數的效果(假設環境不會造成 setTimeout 時間誤差)
1. 給予 setTimeout 執行環境
```javascript=
let i = 0;
while(i++ <5) {
let x = i;
setTimeout(()=> { console.log(x) }, i*1000);
}
```
2. 使用立即函式,鎖住作用域,並帶入參數
```javascript=
let i = 0;
while(i++ <5) {
((i)=>{
setTimeout(()=>{
console.log(i)
}, i*1000);
})(i);
}
```
3. 給予 setTimeout 執行時的參數
```javascript=
let i = 0;
while(i++ <5) {
setTimeout((x)=> { console.log(x) }, i*1000,i);
}
```
<br><br><br><br><br>
###### tags: `JavaScript` `JavaScript 面試考題`