# 2024-07-05 JavaScript
## Promise的解釋 [面試重點]
問: 什麼是`Promise`?
答: 我不知道我什麼時候會好,但是我好了我保證會告訴你,一定會有結果
```js
/*
* Promise
* 有三種狀態:
* pedding : 創建時,預設狀態
* resolve (Fulfilled) : Promise成功完成後會進入的狀態
* reject : Promise失敗後會進入的狀態
*/
```
[pedding情境]
```js
const whatever = function (a, b) {};
const p1 = new Promise(whatever);
console.log(p1);
```

```js
const p1 = new Promise(function (resolve, reject) {
// true: 進入resolve function
// false: 進入reject function
let success = false;
// new Promise的時候會開始執行
console.log("Start Promise...");
// 等待setTimeout時間到,會根據success判斷進入哪一種狀態
// 只會得到一種結果,不會同時發生resolve和reject
setTimeout(() => {
if (success) { <===== 3秒後執行
// true -> then()
resolve("Promise is resole (o))");
} else {
// false -> catch()
reject("Promise is reject (x)");
}
}, 3000);
});
p1.then((r) => {
console.log(r);
}).catch((r) => {
console.log("error", r);
});
```
```js
* async function 內可以有很多個Pr
* await fetch() 所回傳的值會是一個Promise,
使用await去等待Promise的結果(resolve或reject)
* resolve() -> .then()
reject() -> .catch()
* reject()的用意是可以做出一個提示頁面(例如:404),引導使用者到對的地方去
```
## Event loop 事件循環 [面試題]
- Call Stack
- 所有function都會逐一被放到call stack
- FILO先進後出: First In, Last Out
- web Apis
- Callback Queue
- 瀏覽器內的Queue
- FIFO先進先出: First In, First Out (例如:超商排隊結帳)
※ 要等Call Stack內所有工作都做完,才能執行Callback Queue
※ Call Stack在何時會一直累績上去 : 是在function()內在呼叫一層function()在呼叫一層function()...

```
* 同步: 等到每一步都執行完畢,才進行下一步驟
* 非同步: 把需要花時間處理的事情,先丟到另一個地方去
```
```js
// 1. call stack
console.log(1);
// 2. call stack
// 2-1. webAPI -> wait time out
// 4. webAPI -> callback Queue
// 5. callback Queue -> call satack
setTimeout(() => {
console.log(2);
}, 1000);
// 3. call stack
console.log(3);
```
**** callback Queue內的資料一定會等到call stack內沒有任何task,才會從callback Queue放到call stack去執行
```js
// 1.
console.log(1);
// 2. call stack -> webAPI -> wait
// 5. timeout -> webAPI -> callback Queue -> 等待for loop在call stack內執行完畢
// 6. for loop -> end -> 從callback Queue放到call stack上執行
setTimeout(() => {
console.log(2);
}, 1000);
// 3.
console.log(3);
// 4.
for (let i = 0; i < 1000000000; i++) {
}
```
**** 所有非同步執行的函數都會被放進webAPI
## <script></script> 在實務上不見得可以放在body
把<script>放到<head>內可以設定兩種屬性
1. defer => <script src="./app.js" `defer`></script> (所以瀏覽器都支援)
2. async => <script src="./app.js" `async`></script>
參考網址: https://pjchender.dev/javascript/js-async-defer/

`scaript正常情況下`
1. HTML文件解析時,只要遇到`<script>`就會先把`HTML parsing pauseed`
2. 開始下載script檔案
3. 下載完畢後,開始執行
4. HTML恢復parsing

`defer` 會讓 scripts 的檔案先開始被下載,但在 HTML 文件準備好後才開始執行,同時會確保各個 script 檔案執行的順序

`async` 會讓 scripts 的檔案先開始被下載,但它不會確保各個 script 檔案被執行的順序,先下載好的就先執行

addEventListener也是非同步,會走call stack -> web Apis -> wait
等到`有觸發`的時候才會從web Apis -> callback Queue -> call stack 流程去排隊 (callback function去webApi排隊)
## event loop 要看的影片 https://www.youtube.com/watch?v=8aGhZQkoFbQ&ab_channel=JSConf
## Event Flow 事件流 [面試題]
可分為三個階段:
1. 捕獲階段 capturing
2. 目標階段 targeting
3. 冒泡階段 bubbling
[情境一]
```html
<body>
<div class="aa">
<div class="bb"></div>
</div>
<script src="./app.js" async></script>
</body>
```
```js
.aa {
width: 300px;
height: 300px;
background-color: yellow;
display: flex;
align-items: center;
justify-content: center;
}
.bb {
width: 150px;
height: 150px;
background-color: pink;
}
```


### event flow 會由上而下
1. 入口 Capturing
2. 目標 Targeting
3. 出口 Bubbling

### addEventListener(type, listener, useCapture)
- useCapture : false (預設) 註冊在`出口`的地方
- 使用 `Bubbling` 方向的順序 => 由下往上 ↑
- addEventListener(type, listener)

```html
<body>
<div class="aa">
<div class="bb"></div>
</div>
<script src="./app.js" async></script>
</body>
```
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener("click", () => {
console.log("AA");
});
bb.addEventListener("click", () => {
console.log("BB");
});
```

- useCapture : true 註冊在`入口`的地方
- 使用 `Capturing` 方向的順序 => 由上往下 ↓
- addEventListener(type, listener, true)

```html
<body>
<div class="aa">
<div class="bb"></div>
</div>
<script src="./app.js" async></script>
</body>
```
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener(
"click",
() => {
console.log("AA");
},
true
);
bb.addEventListener(
"click",
() => {
console.log("BB");
},
true
);
```

### 停止事件傳播 e.stopPropagation()
- 可以設定`e.stopPropagation()`停止事件傳播,指的是下一個行為不再傳播
[情境一 設定停止傳播在 bb ]
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener("click", (e) => {
console.log("AA");
});
bb.addEventListener("click", (e) => {
e.stopPropagation(); <=== 在BB層設定停止傳播,所以就不會在往AA層
console.log("BB");
});
```

[情境二 在aa監聽設定true,在bb設定停止傳播 ]
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener(
"click",
(e) => {
console.log("AA");
},
true
);
bb.addEventListener("click", (e) => {
e.stopPropagation();
console.log("BB");
});
```


[情境三 設定停止傳播在 aa ]
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener("click", (e) => {
e.stopPropagation(); <=== 在AA層設定停止傳播,但後面沒東西,所以不會有影響
console.log("AA");
});
bb.addEventListener("click", (e) => {
console.log("BB");
});
```

### 獲取Target ( 事件觸發元素,元素也就是最底層彎處的target )
- `target` 是整個事件流的最底層彎處的元素
- 不管`AA層`或是`BB層`都會只會指定到同一個`target`
[情境一 在aa層獲取target]
```html
<body>
<div class="aa">
<div class="bb"></div>
</div>
<script src="./app.js" async></script>
</body>
```
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener("click", (e) => {
console.log(e.target);
^^^^^^^^^^^^^^^^^^^^^^
印出 <div class="aa">
<div class="bb"></div>
</div>
});
```

[情境二 在bb層獲取target]
```html
<body>
<div class="aa">
<div class="bb"></div>
</div>
<script src="./app.js" async></script>
</body>
```
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener("click", (e) => {
console.log(e.target); <=== 印出 <div class="bb"></div>
});
bb.addEventListener("click", (e) => {
console.log(e.target); <=== 印出 <div class="bb"></div>
});
```

### 獲取currentTarget (事件監聽器註冊到的元素都會印出)
[情境一 點擊觸發aa層]
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener("click", (e) => {
console.log(e.currentTarget);
});
bb.addEventListener("click", (e) => {
console.log(e.currentTarget);
});
```

[情境二 點擊觸發bb層]
```js
const aa = document.querySelector(".aa");
const bb = document.querySelector(".bb");
aa.addEventListener("click", (e) => {
console.log(e.currentTarget);
}); <=== 預設註冊在bubbling
bb.addEventListener("click", (e) => {
console.log(e.currentTarget);
}); <=== 預設註冊在bubbling
```

[情境三 設定button,點擊button會將所有div都移除]
```html
<body>
<div class="aa">
<div class="bb">
<button id="btn">X</button>
</div>
</div>
<script src="./app.js" async></script>
</body>
```
```js
const close = document.querySelector("#btn");
close.addEventListener("click", (e) => {
console.log(e.currentTarget.closest(".aa"));
// e.currentTarget表示選擇到我目前這顆button
// e.currentTarget.closest(".aa")表示離我這顆按鈕最近的aa
e.currentTarget.closest(".aa").remove();
});
```

所有的div 都會消失不見 (包含aa層和bb層)

在什麼實際案例下會使用到currentTarget (撲克牌遊戲)

## 物件 (待續)