---
# System prepended metadata

title: 8/21讀書會(JS)
tags: [面試考古題, JavaScript]

---

# 8/21讀書會(JS)
###### tags: `面試考古題` `JavaScript`

## Map
定義：key-value配對的物件，和`object`很相似，但兩者的差別

1. Map中的key值是唯一的，如果`set`到重複的key，舊的value就會被蓋掉
2. Map中的key會根據加入資料的時間而有順序性，但`object`沒有順序
3. `object`的key只能是string或symbols，但`Map`的key則可以是任何值，包含物件、函式、原始型別(primitive type)
4. 要取得`Map`的大小可使用`size`屬性，但`object`則不行
5. 當需要經常增加、刪減屬性時，使用`Map`效能會比`object`好


> ES6 中如果希望「陣列（Array）」的元素不會重複，可以使用 Set；如果是希望物件（Object）的鍵不會重複，則可以使用 Map。


使用語法：
```javascript=
// 建立 Map
let myMap = new Map(); // 建立空的 Map
let myMap = new Map([
  [1, 'one'],
  [2, 'two'],
]); // 建立 Map 時直接代入內容

let keyString = 'a string',
  keyObj = {},
  keyFunc = function () {};

// 透過 .set(key, value) 來在 Map 中添加屬性
myMap.set(keyString, 'value associated with string');
myMap.set(keyObj, 'value associated with object');
myMap.set(keyFunc, 'value associated with function');

// 方法
myMap.has(keyString); // true，透過 .has 判斷該 Map 中是否有某一屬性
myMap.size; //  3，透過 .size 來取得 Map 內的屬性數目
myMap.get(keyString); // 使用 .get(key) 可取得屬性的內容
myMap.delete(keyString); // 刪除 Map 中的某個屬性，成功刪除回傳 true，否則 false
myMap.clear(); // 清空整個 Map
myMap.keys() // 取得Map所有keys
myMap.values() // 取得Map所有values
myMap.entires() // 取得Map所有內容

// Map合併
const merged = new Map([...first, ...second]) // 如果key相同，後面的key會蓋掉前面的

// 也可以和陣列合併
const merged = new Map([...first, ...second, [1, 'foo']]);

// 用Map做hash table
const arr = ['apple', 'apple', 'banana', 'banana', 'cat', 'dog', 'fat', 'fat', 'fat', 'fat']

const hashTable = new Map()

arr.forEach(item => {
    if (hashTable.has(item)) {
        hashTable.set(item, hashTable.get(item) + 1)
    }  else {
        hashTable.set(item, 1)
    }
})
```

[**Reference**](https://pjchender.dev/javascript/js-map/)

## Event delegation 事件委派

* 受惠於Event Bubbling而能減少監聽器數目的方法
* 在父層統一處理event handler
* Event Delegation的用法很簡單，就是將事件監聽器附加到按鈕的"父級"，並在單擊按鈕時捕獲冒泡事件。

```htmlmixed=
<ul id="list">
  <li data-num="1"><em>1</em></li>
  <li data-num="2" class="hello"><em>2</em></li>
  <li data-num="3"><em>3</em></li>
  <li data-num="4"><em>4</em></li>
</ul>
```
```javascript=
list.addEventListener('click', event => {
    if (event.target.matches('.hello')) {
        ....
    }
})
```

## Event loop
![](https://i.imgur.com/WK0hHoi.png)

![](https://i.imgur.com/SOOeip8.gif)


在js中 web api還分為micro task queue 和 macro task queue
* macro task包含：mouse event, keyboard event, network event, HTML parsing
* micro task包含：DOM, Promise

**執行順序**
* js會先執行同步程式碼，當執行完一個macro task後，會檢查micro task中有沒有東西，如果有的話會見縫插針的把所有micro task都執行完，再回去執行macro task


## Ajax (Asynchronous JavaScript and XML)
定義：非單一的技術，是綜合性的瀏覽器端網頁開發技術

目的：處理非同步的情況，發送非同步請求給後端，後端再回傳必要的資料給前端，網頁不用重新整理，就能即時地透過瀏覽器去跟伺服器溝通，撈出資料

常用的三種資料格式
* HTML
* XML
* JSON


# Scoping

## 定義

變數可以被取用(accessibility)或是被看到(visibility)的有效範圍

## 全域作用域

不在函式或區塊內宣告的變數，可以在任何地方取用變數的範圍

- 在最外層直接宣告變數

```jsx
var name = 'tim' // let, const, var都一樣
function call() {
	console.log(name) 
}
call() 
```

- (not recommend)定義變數時沒有使用宣告-使用'use strict'模式，這樣會跳出錯誤

```jsx
function call() {
	name = 'tim'
  console.log(window.name) 
}
call() 
```

## 區域作用域

在特定範圍才可以取用的作用域

### 函式作用域: var

被var宣告的變數，能被取用以及看見的區域為**函式**內。

```jsx
function call() {
	var name1 = 'tim'
	console.log(name1) // tim
}
call() 
console.log(name) // is not defined
```

### 區塊作用域: const, let

被const宣告的變數，能被取用以及看見的區域為**區塊**內

```jsx
{
	var name3 = 'varTim'
	const name4 = 'constTim'
}

console.log(name3) // varTim
console.log(this) // window
console.log(this.name3) // varTim
console.log(name4) // not defined
```

## Scope chain

向外部環境參考(Reference to Outer Environment)尋找需要的變數的行為

```jsx
var name5 = 'timGlobal'

a()

function a() {
	var name5 = 'timInFunca' // All
	b()
}

function b() {
	console.log(name5)
}

// 問a()會產出什麼結果, is not defined, timGlobal, timInFunca
```

這個結果就是scope chain，就是程式碼在搜尋變數時向外找的行為，不過向外找的環境為什麼不是a函式呢?因為scope chain是依據Lexical Environment，而b函式在執行程式時，他在函式內找不到變數，向外之後就直接是全域了。

### 詞彙環境(Lexical Environment)

記錄著程式碼實際上、物理上在哪個位置，和函式們之間的關聯

# Closure 閉包

簡單的說法是函式與函式內的函式形成的環境，函式記得並存取scope的能力(scope chain)。

```jsx
var a = 3

var baz = foo()

baz() // 2

function c() {
	console.log(a)
}

function foo() {
  var a = 2

  function bar() {
    console.log(a)
  }
	
	c() // 3
  return bar
}
```

- JavaScript 引擎的垃圾回收機制會釋放不再使用的記憶體，但閉包為了保留函式記得和存取其語彙範疇的能力，就會予以保留，不做記憶體回收。因此，bar 仍保留指向 foo 的內層範疇的參考，這個參考就是閉包。

## 閉包的使用

- 隱藏**私有變數**讓變數只能被向外開放的介面接觸-按鈕stateA, stateB→state?

```jsx

function GradeCalculater(ch, en, math) {
	let chWeighting = 2
	let enWeighting = 2
	let mathWeighting = 2
	
	function displayGrade() {
		const total = ch * chWeighting + en * enWeighting + math * mathWeighting
		const totalWeight = mathWeighting + enWeighting + chWeighting

		return console.log(total / totalWeight)
	}

	function changeWeight(ch, en, math) {
		chWeighting = ch
		enWeighting = en
		mathWeighting = math
	}

	return {
		displayGrade, changeWeight
	}
}

const timGrade = GradeCalculater(30, 40, 50) 
const benGrade = GradeCalculater(100, 40, 50) 
timGrade.displayGrade() // 40
benGrade.displayGrade() // 63.333333333333336

benGrade.changeWeight(2, 1, 1)
benGrade.displayGrade() // 72.5
timGrade.displayGrade() // 40
```

- ES6之後，個別載入的檔案視為模組，引入的檔案以及被引入的模組視作在同一個閉包內

```jsx
// 檔案路徑 ./route/index.js

const express = require('express')
const router = express.Router()

const { authenticated, checkNotRole } = require('../middlewares/auth')

const admin = require('./modules/admin')

router.use('/api/admin', authenticated, checkNotRole('user'), admin)

module.exports = router
```

```jsx
// 檔案路徑 ./app.js
// 等於含有routes，而routes裡面又含有router
const express = require('express')
const app = express()
const routes = require('./routes')

app.use(routes) // app.js檔裡有一個route.js檔還有router

module.exports = app
```

- 效能考量：使用prototype代替閉包

用閉包來模擬並且存取私有變數。用prototype來實作通用方法以及存取通用的私有變數。

```jsx
function GradeCalculater(ch, en, math) {
	this.chWeighting = 2
	this.enWeighting = 2
	this.mathWeighting = 2
	this.ch = ch
	this.en = en
	this.math = math
}

// 不能用箭頭韓式
GradeCalculater.prototype.displayGrade = function() {
	const {ch, en, math, mathWeighting, enWeighting, chWeighting} = this
	const total = ch * chWeighting + en * enWeighting + math * mathWeighting
	const totalWeight = mathWeighting + enWeighting + chWeighting

	return console.log(total / totalWeight)
}

GradeCalculater.prototype.changeWeight = (ch, en, math) => {
	this.chWeighting = ch
	this.enWeighting = en
	this.mathWeighting = math
}

const timGrade = new GradeCalculater(30, 40, 50) 
const benGrade = new GradeCalculater(100, 40, 50) 
timGrade.displayGrade() // 40
benGrade.displayGrade() // 63.333333333333336
Object.entries(timGrade)

benGrade.changeWeight(2, 1, 1)
benGrade.displayGrade() // 72.5
```

```jsx
// 會不會出現錯誤呢? NO:

function handle(message) {
	let message2 = message
  console.log('1', message);
	return function (message = message2) {
		console.log('2', message)
	}
}

function setAlarm(message, timeout) {
  setTimeout(handle(message), timeout);
}

setAlarm("Wake UP!", 2000);
```

IIFE 是閉包嗎?

~~為什麼settimeout裡面函式加上()會立即執行：因為拿到的是return後的值~~

為什麼return的函式沒辦法取用父函式的參數：因為需要scope被宣告

# Event bubbling

當事件發生在DOM tree上的其中一個node時候，會觸發parent→parent的parent...一直到root

## DOM事件三階段：

可以使用event.eventPhase取得

1. CAPTURING_PHASE
2. AT_TARGET
3. BUBBLING_PHASE

![](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/409df962-211d-4ea8-a8b8-3bb7962aabd7/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAT73L2G45O3KS52Y5%2F20210822%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20210822T031244Z&X-Amz-Expires=86400&X-Amz-Signature=298ca19df59e1928cfff92b2ef36e5fc2d1bd07cef6bd8e99122b5d5c5d72896&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22Untitled.png%22)

[https://blog.techbridge.cc/2017/07/15/javascript-event-propagation/](https://blog.techbridge.cc/2017/07/15/javascript-event-propagation/)

## 實作

![Untitled](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/e01258a9-1ea8-4b1a-9e22-5976db395f1c/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAT73L2G45O3KS52Y5%2F20210822%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20210822T031257Z&X-Amz-Expires=86400&X-Amz-Signature=4daef11d729d5e9487cf70a48caea12bb4b9a4fc6d6779066b881c6d784961a0&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22Untitled.png%22)

```jsx
// list 的捕獲
$list.addEventListener('click', (e) => {
  console.log('list capturing', e.eventPhase);
}, true)

// list 的冒泡
$list.addEventListener('click', (e) => {
  console.log('list bubbling', e.eventPhase);
}, false)

// list_item 的捕獲
$list_item.addEventListener('click', (e) => {
  console.log('list_item capturing', e.eventPhase);
}, true)

// list_item 的冒泡
$list_item.addEventListener('click', (e) => {
  console.log('list_item bubbling', e.eventPhase);
}, false)

// list_item_link 的捕獲
$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link capturing', e.eventPhase);
}, true)

// list_item_link 的冒泡
$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link bubbling', e.eventPhase);
}, false)

// 預期結果?
```

```jsx
// list 的冒泡
$list.addEventListener('click', (e) => {
  console.log('list bubbling', e.eventPhase);
}, false)

// list 的捕獲
$list.addEventListener('click', (e) => {
  console.log('list capturing', e.eventPhase);
}, true)

// list_item 的冒泡
$list_item.addEventListener('click', (e) => {
  console.log('list_item bubbling', e.eventPhase);
}, false)

// list_item 的捕獲
$list_item.addEventListener('click', (e) => {
  console.log('list_item capturing', e.eventPhase);
}, true)

// list_item_link 的冒泡
$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link bubbling', e.eventPhase);
}, false)

// list_item_link 的捕獲
$list_item_link.addEventListener('click', (e) => {
  console.log('list_item_link capturing', e.eventPhase);
}, true)

// 預期結果?
```

# jQuery

是一個簡化瀏覽器API的JS函式庫，把JS包裝成更白話更迅速的寫法 + 豐富套件。

因為原本的瀏覽器API非常簡略，所以在寫複雜的邏輯時就需要「手動做輪子」將複雜的邏輯封裝成簡單的function再呼叫。

舉例來說起初的瀏覽器腳本只有以下API可以存取node

- document.getElementById()
- document.getElementsByTagName()
- document.getElementsByClassName()

如果你想取用的是ul li之類的要怎麼做呢?

統一了過去IE、瀏覽器們不同規範下造成的開發困擾

## 特性

- HTML 元素選取
- HTML 元素操作
- CSS 操作
- HTML 事件函式
- JavaScript 特效和動畫
- HTML DOM 遍歷和修改
- AJAX
- Utilities

## 弱化的原因

W3C制定了統一規範，以及除了IE外的瀏覽器進話，jQuery最大的對手其實是瀏覽器API的更新，比如目前已經有querySelector以及querySelectorAll等API可以更簡潔的存取node。

## 劣勢

也因為他是用過去的瀏覽器API搭配JS，他較慢、較龐大。

- 控管難度高：沒有框架的介入、限制，如何提高維護性全看開發者能力

## 優勢

不適合打造類似Gmail、FB等級複雜又講究效能的高端應用，但要快速開發一般企業內部系統，已經足夠，另外學習成本相較其他主流前端框架更是相當低。

## 推薦使用

快速開發：vue.js MVVM搭配jQuery

# DOM
* 全名：Document Object Model
* 定義：是一個將 HTML 文件以樹狀的結構來表示的模型，而組合起來的樹狀圖，我們稱之為「DOM Tree」，是給 HTML & XML 文件使用的一組 API ，本質是建立網頁與 Script 或程式語言溝通的橋樑。
* DOM Tree 共包含4個類型節點
    * 文件節點 `document`：  HTML 檔的開端，所有的一切都會從 Document 開始往下進行。
    * 標籤元件節點 `Element`：文件內的各個標籤，因此像是`<div>、<p>` 等等各種 HTML Tag 都是被歸類在 Element 裡面。
    * 屬性節點 `attribute`：Attribute 就是指各個標籤內的相關屬性
    * 文字節點 `text`：被各個標籤包起來的文字，舉例來說在 `<h1>Hello World</h1>` 中， Hello World 被 `<h1>` 這個 Element 包起來，因此 Hello World 就是此 Element 的 Text
* DOM API：JavaScript 可以藉由DOM API去存取並改變HTML架構、樣式和內容的方法。
* 範例說明，以下程式碼轉變成的DOM Tree：
    ```html
    <html>
      <head>
        <title>example</title>
      </head>
      <body>
        <h1 class="txt">Hello World</h1>
      </body>
    </html>
    ```
    ![](https://i.imgur.com/axU6rLY.png)

# Unobtrusive JavaScript 非侵入式JavaScript

* 定義：Unobtrusive Javascript是一種將Javascript從HTML結構抽離的設計概念，避免在HTML標籤中夾雜onchange、onclick Attribute掛載Javascript事件，讓HTML歸HTML、Javascript歸Javascript，功能權責清楚區分，HTML也變得清爽容易閱讀。
```jsx=
<input type="text" name="date" onchange="validateDate()"/>
```
// Unobtrusive JavaScript 會改為這樣
```jsx=
<input type="text" name="date" id="date" />
window.addEventListener("DOMContentLoaded"，function（event）{
    document.getElementById('date').addEventListener("change"，validateDate);
});
```
* 其中概念也強調：重要的網站內容應該都要能用純ＨＴＭＬ來表示，使用多的ＪＳ是為了讓使用者有更好的體驗！
* 可以參考的詳細說明：https://www.w3.org/wiki/The_principles_of_unobtrusive_JavaScript

# prototype / Prototypal inheritance
1. prototype：每一個透過建構式函式產生的物件，都會攜帶一個原型物件。原型物件也有著自己的原型，於是原型物件就這樣鏈結，直到撞見 null 為止：null 在定義裡沒有原型、也是原型鏈（prototype chain）的最後一個鏈結。
2. Prototypal inheritance：**核心目的是「取得另一個物件的屬性與方法」**
JavaScript 的繼承系統會透過原型鍊來繼承原型物件的方法，因此稱為「原型繼承」，「繼承」的物件並不會一併複製功能過來，而是透過原型鍊連接其所繼承的功能。
> 感覺用英文解釋比較順：All JavaScript objects inherit properties and methods from a prototype:
> 