---
# System prepended metadata

title: 2024-07-05 JavaScript
tags: [JavaScript]

---

# 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);
```
![image](https://hackmd.io/_uploads/SJmdPCwPC.png)


```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()...

![image](https://hackmd.io/_uploads/HyVjI0LDR.png)


```
* 同步: 等到每一步都執行完畢，才進行下一步驟
* 非同步: 把需要花時間處理的事情，先丟到另一個地方去
```

```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/

![image](https://hackmd.io/_uploads/HkLyjRIwC.png)


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

![image](https://hackmd.io/_uploads/ByUxi0Iw0.png)


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

![image](https://hackmd.io/_uploads/SyiWsALPC.png)


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

![image](https://hackmd.io/_uploads/r1tfsA8wA.png)

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;
}
```
![image](https://hackmd.io/_uploads/BJaXX0vP0.png)
![image](https://hackmd.io/_uploads/B1UH9G_vA.png)


### event flow 會由上而下

1. 入口 Capturing
2. 目標 Targeting
3. 出口 Bubbling

![image](https://hackmd.io/_uploads/rJZQHMuwC.png)

### addEventListener(type, listener, useCapture)
- useCapture : false (預設) 註冊在`出口`的地方
    - 使用 `Bubbling` 方向的順序 => 由下往上 ↑
    - addEventListener(type, listener)
    ![image](https://hackmd.io/_uploads/S1mq5MuDR.png)

    ```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");
    });
    ```
    ![image](https://hackmd.io/_uploads/SyvvaM_PA.png)


- useCapture : true 註冊在`入口`的地方
    - 使用 `Capturing` 方向的順序 => 由上往下 ↓
    - addEventListener(type, listener, true)
    ![image](https://hackmd.io/_uploads/SkVxX7OPA.png)

    ```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
    );
    ```
    ![image](https://hackmd.io/_uploads/HJ2i6fOPC.png)

### 停止事件傳播 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");
});
```

![image](https://hackmd.io/_uploads/rJOJ1X_vA.png)


[情境二 在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");
});
```
![image](https://hackmd.io/_uploads/HJ-q-7OwC.png)
![image](https://hackmd.io/_uploads/HJA4Q7uPR.png)


[情境三 設定停止傳播在 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");
});
```
![image](https://hackmd.io/_uploads/BJhDl7ODA.png)

### 獲取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>
});

```
![image](https://hackmd.io/_uploads/rk0tBQdwC.png)

[情境二 在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>    
});

```
![image](https://hackmd.io/_uploads/rJaZLmdwA.png)

### 獲取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);
});
```
![image](https://hackmd.io/_uploads/B1jBjQdDC.png)

[情境二 點擊觸發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    
```
![image](https://hackmd.io/_uploads/H1_dim_DC.png)

[情境三 設定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();
});
```

![image](https://hackmd.io/_uploads/HJ2MgVdD0.png)

所有的div 都會消失不見 (包含aa層和bb層)
![image](https://hackmd.io/_uploads/BkUVg4OvR.png)

在什麼實際案例下會使用到currentTarget (撲克牌遊戲)
![撲克牌](https://hackmd.io/_uploads/rktnx4_D0.png)


## 物件 (待續)