---
# System prepended metadata

title: JavaScript
tags: [programming, JavaScript]

---

# JavaScript
###### tags: `JavaScript` `programming`
## 目錄
[TOC]
## 參考資料
[1. Understanding ECMAScript 6 (ES6)](https://github.com/nzakas/understandinges6)
[2. 從ES6開始的JavaScript學習生活](https://eddy-chang.gitbook.io/javascript-start-from-es6/)
## 1、簡介

## 2、JavaScript

## 3、變數
- 避免重複的計算
- 抽象化獨立每一層結果
- 儲存使用到的中途狀態
### <i class="fa fa-square" aria-hidden="true"></i> 資料型態
- Undefined
var 宣告變數後沒有給值的變數，這樣的變數內含一個值，就是undefined
- Null
null 型態只有一個值，就是 null，表示缺少資料
- Boolean
true 或 false 通常做為邏輯或條件運算式回傳的結果
- String
字串，由字元組成，必須包含在成雙的「"」或「'」之內
- Number
數值，包括整數或浮點數
- Object
代表這是一個物件
### <i class="fa fa-square" aria-hidden="true"></i> 不同型態變數
- 數字會相加，文字會相接
```javascript=
50 * 3
// 150
"NT" + 50*3 + "$"
// NT 150 $
50 > 30
// true
parseInt("50") + 30
// 80
```
### <i class="fa fa-square" aria-hidden="true"></i> 變數規則
- 首字
    - 必須是英文字母或底線(_)，不可以是數字或其他符號
- 名稱
    - 不可以有空格
    - 大小寫英文字母
        - X1 和 x1 是不同的變數。
    - 不能使用關鍵字
        - JavaScript 內建名字，如: if、else、true、false…
    - 變數需有語意化 (建議)
### <i class="fa fa-square" aria-hidden="true"></i> undefined V.S. is not defined
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> undefined - 宣告變數未給定數值
```javascript=
var value;
console.log(value);
/*------------------------------------------------------
於主控台顯示Undefined
------------------------------------------------------*/
```

#### <i class="fa fa-arrow-right" aria-hidden="true"></i> is not defined - 未宣告變數
```javascript=
.console.log(value);
/*------------------------------------------------------
在未宣告變數value情況下，會得到value is not defined
------------------------------------------------------*/
```
### <i class="fa fa-square" aria-hidden="true"></i> 作用域(Scope)與提升(Hoisting)
根據變數使用有效範圍的不同，可區分為兩種
1. 全域變數
     - 在==函式外==宣告的變數
     - 過多的全域變數會占用記憶體空間，影響程式計算
2. 區域變數
    - 在==函式內==宣告的變數
    - 執行完函式後的區域變數會自動被消除
:::warning
JavaScript程式語言的作用範圍，是使用==函式作用範圍(Function Scope)==，或稱之以函式為基礎(Function-based)的作用範圍。
只有使用函式才能劃出一個本地端的作用範圍，其他的區塊像if、for、switch、while等等，雖然有使用區塊語句({...})，但卻是無法界定出作用範圍。
:::
程式碼：
```javascript=
function scope() {
    if (true) {
      var value = "local";
    }
    console.log(`value in function:`,value);
}
scope();
console.log(`value:`,value);
```
執行結果：
```shell=
value in function: local
Uncaught ReferenceError: value is not defined 
```
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> 變數提升(Hoisting)機制
在函數作用域或全局作用域中，透過關鍵字var宣告的變數，**無論在哪裡宣告的**，都會被當成在==當前作用域頂端宣告的變數==。
程式碼：
```javascript=
if(true) {
  var if_value;
  console.log(`if_value:`, if_value);
}
console.log(`global:`, if_value);
// 等同於以下寫法
/*-------------------------------------
var if_value;
if(true) {
  console.log(`if_value:`, if_value);
}
console.log(`global:`, if_value);
-------------------------------------*/
```
執行結果：
```shell=
if_value: undefined
undefined
```
### <i class="fa fa-square" aria-hidden="true"></i> var宣告中的遮蔽效應(Shadowing)
var 可以讓同一個==變數名稱==，在作用域中被定義很多次
程式碼：
```javascript=
function showMSG() {
    var msg = "Good";
    alert(msg);
}
var msg = "Hello";
showMSG();
alert(msg);
```
程式碼：
```javascript=
var foo="bar";
var foo ="abc";
console.log(foo);
// abc
```
### <i class="fa fa-square" aria-hidden="true"></i> let、const變數宣告
- let、const 屬於==區塊級作用域(Block Scope)==
- 不會有Hoisting機制
- 相同作用域中禁止重複宣告變數
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> let宣告
- 用法與var相同，用let代替var來宣告變數，可以把變數的作用域限制在區塊中
```javascript=
if(true) {
  let if_value = "Hello";
}
console.log(`global:`, if_value);
```
執行結果：
```shell=
Uncaught ReferenceError: if_value is not defined 
```
- 禁止重複宣告避免發生遮蔽效應(Shadowing)
    程式：
    ```javascript=
    var count = 30;
    let count = 40;
    ```
    執行結果：
    ```shell=
    Uncaught SyntaxError: Identifier 'count' has already been declared 
    ```
    程式：
    ```javascript=
    var count = 30;
    // Does not throw an error
    if (true) {
        // 只作用於if區塊中
            let count = 40;
        // more code
    }
    ```
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> const宣告
1. 使用const宣告的是常數(constant)，其值一但被設定後==不可更改==
2. const宣告常數必須進行初始化(指派數值)
```javascript=
// 有效的const宣告
const maxValue = 30;

// 語法錯誤：常數未初始化
const minValue;
// Uncaught SyntaxError: Missing initializer in const declaration 
```
```javascript=
if(true) {
    // 此處會拋出錯誤
    // Uncaught ReferenceError: Cannot access 'maxValue' before initialization 
    console.log(maxValue);
    let maxValue = 5;
}
// 此處無法訪問maxValue
console.log(maxValue);
```
#### :star:【補充】const原理與例外狀況
- const原理
JavaScript在對變數的參考進行讀取時，會從變數目前對應的記憶體位址讀取內容，當使用者改變變數的數值時，引擎會重新從記憶體中分配一個新的記憶體空間以儲存新的值，並將新的記憶體位址與變數進行綁定。
const的原理便是在變數名稱與記憶體之間建立不可變的綁定，當後面的程式嘗試申請新的記憶體空間時，引擎便會拋出錯誤。
- const 例外狀況
const的實現原理，確實只限於創造一個不可變的記憶體綁定，而在某些情況下，並非**值不可變**。如字串、數字、Boolean、undefined等基礎類型，這些類型的值，在記憶體空間中是不被拆分的；而對於==陣列、物件==等類型，在記憶體中可能會被拆分成許多段落。
:::danger
:loudspeaker: 所以物件類型的變數使用const**可以修改**
:::
```javascript=
const a = 2;
a = 3;
// Uncaught TypeError: Assignment to constant variable. 
const arr = [1, 2, 3, 4];
arr.push(5);
// arr變成[1,2,3,4,5]
const obj = {
    a: 1
};
obj.b = 2;
// obj變成 {a:1, b:2}

```
## 4、流程控制判斷 ( 運算子、 if 、 switch )
### <i class="fa fa-square" aria-hidden="true"></i> 比較運算子
| **符號** | **意義** |  **說明** |
| -------- | -------- |-------- |
| ==     | 相等     |判別運算子左邊是否等於右邊|
| ===     | 相等(嚴謹)     | 判別運算子左邊「型別」與「數字」是否完全等於右邊|
| !=     | 不相等     | 判別運算子左邊是否完全等於右邊|
| !==     | 不相等(嚴謹)     | 判別運算子左邊「型別」與「數字」是否完全不等於右邊|
| true     | 為真     | 判別式為真|
| false     | 為假     | 判別式為假|
| <     | 小於     | 判別運算子左邊是否小於右邊|
| >    | 大於     | 判別運算子左邊是否大於右邊|
| <=    | 小於等於     | 判別運算子左邊是否小於等於右邊|
| >=    | 大於等於     | 判別運算子左邊是否大於等於右邊|

### <i class="fa fa-square" aria-hidden="true"></i> 邏輯運算子
| **符號** | **意義** |  **說明** |
| -------- | -------- |-------- |
| !     | not     |將邏輯值相反，輸入true得到false|
| &&     | and    | 當運算子左右邊均為true時才輸出true|
|  \|\|    | or    | 當運算子左右邊其中之一為true時即輸出true|

### <i class="fa fa-square" aria-hidden="true"></i> 流程判斷 if -else if - else
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> if - else
![](https://i.imgur.com/jvHndNn.png)
```javascript=
let confirmed = true;
if(confirmed) {
    console.log("有朋友");
} else {
    console.log("我是邊緣人");
}
```
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> if - else if - else
![](https://i.imgur.com/ERG3URg.png)
```javascript=
let count = 0;
if(count === 1) {
    console.log("學期成績60分");
} else if(count === 2) {
    console.log("學期成績75分");
} else if(count === 3) {
    console.log("學期成績90分");
} else {
    if(count === 0) {
        console.log("學期成績50分");
    } else {
        console.log("學期成績100分");
    }
}
```
### <i class="fa fa-square" aria-hidden="true"></i> switch條件結構
- 根據不同的變數值執行不同的程式區塊
    - 出現不在設定條件值中的例外情況，則==執行default的程式區塊==
    - 如不會有例外情況出現，則可以省略default程式區塊
        - 以case訂定條件值
        - 每個條件值，需加入`break`跳出條件結構
            - 若符合條件值卻無使用break，則無視後續case直接執行程式區塊
```javascript=
let count = 0;
switch(count) {
    case 1:
    console.log("學期成績60分");
    break;
    case 2:
    console.log("學期成績75分");
    break;
    case 3:
    console.log("學期成績90分");
    break;
    default:
    if(count === 0) {
        console.log("學期成績50分");
    } else {
        console.log("學期成績100分");
    }
    break;
}
```
## 5、陣列與物件
:::info
在JavaScript中支援的型別，主要可以分成==基本型別(Primitives)== 與 ==物件型別(Object)== 兩大類，基本型別如[3.變數](#資料型態)中提到string、number、boolean、null、undefined，symbol(ES6之後)，其餘的可以歸類至物件型別如陣列、物件。
- 基本型別只能代表一個值
- 物件代表多個或複合值
:::
### <i class="fa fa-square" aria-hidden="true"></i> 陣列
- 陣列是由多個元素所組成
- JavaScript 支援一維或多維陣列
- 陣列是==有順序性==的集合
- 陣列大小不固定，可以隨時新增或移除元素
- 陣列內容不一定是同一種資料型態
- 陣列==索引(index)從0開始==，也就是陣列的第一個元素是元素0
- 使用 ==變數[index]== 來提取陣列中的元素
:::danger
:dart: **重要**：
提取陣列元素是從0開始不是從1開始，所以提取第一個元素是**0**，最後一個元素是**length-1**
:::
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> 陣列建立方式 I - Array建構式 (很少用)
```javascript=
let a1 = new Array(); // 空陣列
a1[0] = "apple";
a1[1] = "boy";
a1[2] = "cat";
//此時 a1陣列為 ["apple", "boy", "cat"]
let a2 = new Array(1,2,3); // [1, 2, 3]
let a3 = new Array(2); //長度為2的陣列(所有元素都是undefined)
let a4 = new Array("2"); //["2"]
console.log("al array:",a1);
console.log("a2 array:",a2);
console.log("a3 array:",a3);
console.log(a3[0]);
console.log(a3[1]);
console.log("a4 array:",a4);
a3[2] = "a";
```
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> 陣列建立方式 II - 陣列常值 (Array Literal)
- 此種方式簡潔、方便，實務上比較常使用此方式建立陣列
```javascript=
let a1 = [];
a1[0] = "apple";
a1[1] = "boy";
a1[2] = "cat";
//此時 a1陣列為 ["apple", "boy", "cat"]
a1.length; // 3
// 也可以這樣宣告
let a2 = ["apple", "boy", "cat"];
a2.length;    // 3
// 存取元素
a2[0]; // 存取第一個元素 apple
a2[1]; // 存取第二個元素 boy
a2[2]; // 存取第三個元素 cat，此處也是最後一個元素等同於a3[length-1]
a2[a2.length-1]; // 存取最後一個元素也就是第三個元素

// 增加元素 (陣列大小)
a2[3] = "dog";
a2; // ["apple", "boy", "cat", "dog"]
a2[6] = "fish"; // 中間新增之元素因為指派所以為undefined
a2; // ["apple", "boy", "cat", "dog", undefined, undefined, "fish"];
// 修改元素
a2[1] = "girl"; // 第二個元素從boy修改成girl
a2; // ["apple", "girl", "cat", "dog", undefined, undefined, "fish"];
```
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> push、pop、unshift、shift 在開頭或結尾處添加或移除一個元素
 - 在結尾添加或移除一個元素
     - push()結尾添加一個元素，並回傳==陣列最新長度==
     - pop()結尾移除一個元素，並回傳==被移除的元素==
 - 在開頭添加或移除一個元素
     - unshift()開頭添加一個元素，並回傳==陣列最新長度==
     - shift()開頭移除一個元素，並回傳==被移除的元素==
```javascript=
let arr = ["b", "c", "d"];
arr.push("e");       // 回傳陣列最新長度4；arr目前變成["b", "c", "d", "e"]
arr.pop();           // 回傳"e"；arr目前變成["b", "c", "d"]
arr.unshift("a");    // 回傳陣列最新長度4；arr目前變成["a", "b", "c", "d"]
arr.shift();         // 回傳"a"；arr目前變成[b", "c", "d"]
```
#### <i class="fa fa-arrow-right" aria-hidden="true"></i> concat 在結尾處加入多個元素
concat方法可在陣列加入多個元素並回傳副本，也就是說concat並不影響原陣列
```javascript=
let arr = [1, 2, 3];
arr.concat(4,5,6);        // 回傳[1, 2, 3, 4, 5, 6]; arr並未被修改
arr.concat([4,5,6]);      // 回傳[1, 2, 3, 4, 5, 6]; arr並未被修改
arr.concat([4,5],6);      // 回傳[1, 2, 3, 4, 5, 6]; arr並未被修改
// concat只會直接拆開你提供給他的陣列(如上述狀況)，不會拆開陣列中的陣列(如底下範例)
arr.concat([4,[5,6]]);    // 回傳[1, 2, 3, 4,[5, 6]]; arr並未被修改
```
#### :star::star: 【補充】陣列的操作 forEach、map、filter、reduce
陣列處理技巧是 JavaScript 中非常重要的一塊，尤其是Node.js問世後，JavaScript可以做為前後端開發的程式語言，大量的資料從後端取得或是從前端傳輸，這些資料通常都以陣列物件方式在拋轉，只要能夠對於陣列資料操作熟悉，配合框架就能夠將開發效率更上層樓。
> 資料集來源：https://data.cdc.gov.tw/dataset/aagsdctable-day-19cov

##### <i class="fa fa-caret-right" aria-hidden="true"></i> [forEach()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
forEach 是這幾個陣列函式最單純的一個，不會額外回傳值，只單純執行每個陣列內的物件或值
- 等同於使用for迴圈執行陣列操作
```javascript=
let classes = [
    {
        className: "網際網路應用",
        class: "機械四甲",
        teacher: "丁丁",
        students: 40
    },
    {
        className: "MATLAB",
        class: "機械一甲",
        teacher: "丁丁",
        students: 92
    },
    {
        className: "工業大數據",
        teacher: "鍾文仁",
        students: 30
    }];
//傳統陣列操作
for (let i =0; i < classes.length; i++) {
    console.log(classes[i].className);
}
//forEach 作法
classes.forEach(function(ele,idx) {
     console.log(`forEach className: ${ele.className}`);
     ele.classCode = `MEXXX${idx}`;
})
console.log(classes);

```
##### <i class="fa fa-caret-right" aria-hidden="true"></i> map()
使用 map() 時他需要回傳一個值，他會透過函式內所回傳的值組合成一個陣列
- 如果不回傳則是 undefined
- 回傳數量等於原始陣列的長度
- 很適合將原始的變數運算後重新組合一個新的陣列
```javascript=
// newArr處理完以後的新陣列
// function舊陣列要處理的函數
// value正在處理的元素
// index正在處理的元素索引 (可省略)
// array舊陣列 (可省略)
let newArr = arr.map(function (value, index, array){
  //...
});
```
假如我是一個包租婆，陣列A儲存我每間房收的房租，但我最近比較缺錢所以打算漲個價斂財，每間房子直接改收兩倍的租金，這時候就可以使用 map() 方法快速計算每間房子漲價後的房租，並儲存在B陣列中：
```javascript=
let A = [9000, 8500, 5500, 6500];
let B = A.map(function (value, index, array) {
    return value*2;
});

console.log(A) // [9000, 8500, 5500, 6500] - 原陣列不會被修改
console.log(B) // [18000, 17000, 11000, 13000] - 發財摟 ^____^
```
##### <i class="fa fa-caret-right" aria-hidden="true"></i> filter()
filter() 會回傳一個陣列，其條件是 return 後方為 true 的物件，很適合用在搜尋符合條件的資料
```javascript=
let newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
// element（必備）原陣列目前所迭代處理中的元素。
// index目前所迭代處理中的元素之索引。
// array選擇性呼叫 filter 方法的陣列。
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
let filter_result = words.filter(word => word.length > 6);
console.log(filter_result); // [ 'exuberant', 'destruction', 'present' ]
```
##### <i class="fa fa-caret-right" aria-hidden="true"></i> reduce()
```javascript=
let newArray = arr..reduce(callback[accumulator, currentValue, currentIndex, array], initialValue)
```
reduce() 和其他幾個差異就很大了，他可以與前一個回傳的值再次作運算，參數包含以下：
- accumulator: 前一個參數，如果是第一個陣列的話，值是以另外傳入或初始化的值
- currentValue: 當前變數
- currentIndex: 當前索引
- array: 全部陣列
:::info
簡單來說就是 reduce 方法跟其他陣列方法（例如：map、filter）的差別是它會 return 一個值，而不是一個新陣列
:::
```javascript=
const arr = [1, 2, 3, 4, 5];
const reduceArr = arr.reduce(function(accumulator, currentValue) {
  console.log(`accumulator:${accumulator}`); // 1, 3, 6, 10
  console.log(`currentValue:${currentValue}`); // 2, 3, 4, 5
  return accumulator + currentValue
});
console.log(`reduceArr: ${reduceArr}`);
```
![](https://i.redd.it/9dm5u0f5mrr41.jpg)
出處：[JavaScript reduce function](https://ithelp.ithome.com.tw/articles/10243053)

### <i class="fa fa-chevron-circle-right" aria-hidden="true"></i> 物件
- 物件可以代表多個或複合值
- 實質上物件是一種==容器==
- 物件內容稱為屬性(Property)，是由名稱(鍵)與值組成
- 陣列是有序的，物件是無序的
物件宣告方式：
```javascript=
// 大括號成對存在，用來表達物件的內容
// 建立物件與內容方式I
let mobile = {
    brand: "Samsung",
    model: "S22",
    color: "White"
};  
// 建立物件與內容方式II
let mobile2 = {}; // 空物件
mobile.brand = "Apple";
mobile.model = "iphone 13 pro";
mobile["color"] = "Gold";

console.log(mobile);
console.log(mobile2);

```
物件的運用
```javascript=
let course = {
    teacher: "阿奇",
    className: "網際網路應用",
    students: ["貝蒂", "羅利", "諾麗", "塔格", "海皮"],
    classRoom: "工206"
};
```
## 6、迴圈
### 迴圈基本操作
- 迴圈結構必須包含判別式及條件變數
    - 初始迴圈變數
    - 迴圈條件
    - 遞增迴圈變數
```javascript=
//初始狀態;條件;遞增
for(let i = 0; i < 3; i++) {
    console.log(i);
}
```
### 迴圈與陣列物件
```javascript=
let classes = [
    {
        className: "網際網路應用",
        class: "機械四甲",
        teacher: "丁丁",
        students: 40
    },
    {
        className: "MATLAB",
        class: "機械一甲",
        teacher: "丁丁",
        students: 92
    },
    {
        className: "工業大數據",
        teacher: "鍾文仁",
        students: 30
    }];

```
## 7、函式
### 參考
- [重新認識 JavaScript: Day 10 函式 Functions 的基本概念](https://ithelp.ithome.com.tw/articles/10191549)
- JavaScript學習手冊
### 函式寫法
- 基本函式
```javascript=
//宣告函式 函式名稱(){}
function calculate() {
    alert("執行計算函式");
    alert("請問你要計算什麼");
}
// 執行函式
calculate();
```
- 函式帶參數(引數)
```javascript=
function calculate(num){
    let total = num * 25;
    alert("計算後的數值為"+total);
}
calculate(6);
```
:::warning
引數只會在函式裡生存，就算在函式外有相同的名稱
:::
```javascript=
function f(x) {
    console.log("inside f: x = " + x);
    x = 5;
    console.log("inside f: x = " + x +"(after assignment)");
}
let x = 3;
console.log("before calling f: x = "+ x);
f(x);
console.log("after calling f: x = "+ x);
```
#### 預設引數
在ES6中有一個新功能，可以指派引數的預設值，一般來說，當我們沒有提供引數的數值時，我們會得到undefined值
```javascript=
function f(a, b="default", c = 3) {
    /*----
    初始：let a, b = "default", c = 3;
    f() => a; b = "default"; c = 3;
    f(5) =>  a = 5; b = "default"; c = 3;
    f(5,6) =>  a = 5; b = 6; c = 3;
    f(5,6,7) =>  a = 5; b = 6; c = 7;
    -----*/
    console.log(a+"-"+b+"-"+c);
}
f();
f(5);
f(5, 6);
f(5, 6, 7);
```
- 函式返回結果
```javascript=
function getCalResult(num) {
    let returnValue = num * 12;
    return returnValue;
}
let getResult = getCalResult(12);
alert("getResult Value:"+getResult);
```
### 呼叫 V.S. 參考
在JavaScript中，函式是物件，因此如同其他的物件資料型態，可以將它到處傳遞與賦值
- 當我們在函式名稱後面加上括號例如calculate()時，JavaScript就知道我們要==呼叫==這個函數
- 當我們在函式名稱後面沒有加括號時，只是參考函式，與其他任何值一樣，==函式不會被呼叫==
    - 這點讓JavaScript有更多彈性，比如說可以將函式指派給一個變數，我們就可以用其他名稱來呼叫這個函式
```javascript=
function getGreeting() {
    console.log("hello world!");
}
getGreeting();
getGreeting;

//----參考----
console.log("將函式指派給refFunction這個變數");
const refFunction = getGreeting;
refFunction();
//將函式放入物件中
let obj = {};
obj.f = getGreeting;
console.log("執行物件內函式");
obj.f();
// 將函式放入陣列中
let arr = [1, 2, 3];
arr[2] = getGreeting;
console.log("執行陣列內元素的函式");
arr[2]();
```
### 提升 Hoisting
除了==var==這個關鍵字使得變數有提升機制外，函式也具有提升的機制
```javascript=
square(2);    // 4
function square(number) {
  console.log(number * number);
}

```
### 函式運算式（Function Expressions）
透過 ==變數名稱 = function([參數]){ ... };== 的方式，將一個函式透過 = 指定給某個變數
```javascript=
var square = function (number) {
  console.log(number * number);
};
```
:::warning
函式運算式**不具提升作用**
:::
```javascript=
square(2); // Uncaught TypeError: square is not a function 
var square = function (number) {
  console.log(number * number);
};
```
## 8、DOM
### 什麼是DOM
#### 參考資料
- [什麼是 DOM ？](https://yuwensaf.github.io/9c95b00e/)
- [w3scools - HTML DOM Documents](https://www.w3schools.com/jsref/dom_obj_document.asp)
##### 這是一個html文件
![](https://i.imgur.com/FqcrTFn.jpg)
##### 轉換成DOM
![](https://i.imgur.com/DmvuqBx.jpg)


### getElementById、querySelector、querySelectorAll
1. getElementById、querySelector可以選取到一個節點標籤
2. querySelectorAll可以選取到多個符合條件的節點資料，該資料會是一個**陣列資料**
- HTML
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="css/style.css">
    <title>Document</title>
</head>
<body>
    <h1 id="titleId" class="titleClass">title</h1>
    <h2>標題1</h2>
    <h2>標題2</h2>
    <h2>標題3</h2>
    <h2>標題4</h2>
    <h2>標題5</h2>
    <script src="js/all.js"></script>
</body>
</html>
```
- JavaScript
```javascript=
/*
     1.用Id取元素 → #Id
     2.用Class取元素 → .Class名稱
     3.用標籤取元素 → 標籤
*/
var el = document.querySelector('.titleClass em');
el.textContent ='123';

// querySelectorAll 會回傳陣列資料
let titles = document.querySelectorAll('h2');
titles.forEach(function(el, idx) {
  el.textContent = `物件${idx+1}`
})

```
### 屬性的設定與取得
#### setAttribute
:::info
element.setAttribute(name, value)
:::
#### getAttribute
:::info
Element.getAttribute(attributeName)
:::
### 操控HTML
#### innerHTML
- 方法 : 組完字串後，傳進語法進行渲染。
- 優點 : **清空**在上，效能快。
- :star:缺點 : 資安危險，要確保來源，不然容易發生==xss==攻擊。

HTML
```htmlembedded=
<!DOCTYPE html>
<html>
    <body>
        <div id="main" class="container">google</div>
    </body>
</html>
```
JavaScript
```javascript=
// let mainEl = document.getElementById("main");
let mainEl= document.querySelector('#main');
// mainEl.innerHTML = "<a href='https://www.google.com/''>前往google網站</a>";
```
實務應用
- HTML
```htmlembedded=
<!DOCTYPE html>
<html>
    <body>
        <div id="main" class="container">
            <ul id="list" class="faculty">
                
            </ul>
        </div>
    </body>
</html>
```
- JavaScript
```javascript=
let facultyList = [
    {
        name: "陳夏宗",
        title: "教授"
    },
    {
        name: "許政行",
        title: "教授"
    },
    {
        name: "康淵",
        title: "教授"
    },
    {
        name: "鍾文仁",
        title: "教授"
    },
    {
        name: "丁鏞",
        title: "教授"
    },
    {
        name: "張耀仁",
        title: "教授"
    },
    {
        name: "陳冠宇",
        title: "教授"
    },
    {
        name: "范憶華",
        title: "教授"
    },
    {
        name: "李有璋",
        title: "教授"
    },
    {
        name: "翁輝竹",
        title: "教授"
    },
    {
        name: "黃信行",
        title: "教授"
    },
    {
        name: "吳政達",
        title: "教授"
    },
];
let listLen = facultyList.length;
let ulEl = document.querySelector(".faculty");
// 等同於document.querySelector("#list");
let htmlStr = "";
for(let i = 0; i < listLen; i++){
    ulEl.innerHTML = "<li>"+facultyList[i].name+"<em>"+facultyList[i].title+"</em></li>";
}
```
- CSS
```css=
.faculty li {
    margin: 10px;
    font-size: 24px;
}
.faculty li em {
    color: blue;
}
```
#### [codepen完整程式](https://codepen.io/augustting/pen/gOvdPvQ)

#### createElement
- 方法 : 用 DOM 節點來處理。
- 優點 : 安全性高。
- 缺點 : 效能差。
:::info
需使用==appendChild==將Element加入父節點中
:::
HTML
```htmlembedded=
<!DOCTYPE html>
<html>
    <body>
        <div id="main" class="container">
            <h4>工學院</h4>
            <ul id="depts"></ul>
        </div>
    </body>
</html>
```
JavaScript
```javascript=
// let mainEl = document.getElementById("main");
let mainEl= document.querySelector('#list');
// mainEl.innerHTML = "<a href='https://www.google.com/''>前往google網站</a>";
```
#### [實務應用](https://codepen.io/augustting/pen/oNEEerx)
- HTML
```htmlembedded=
<html>
    <body>
        <div id="main" class="container">
            <ul id="list">
                <li><a href="https://www.google.com">Google</a></li>
            </ul>
        </div>
    </body>
</html>
```
- JavaScript
```javascript=
const deptList = [
  {
    deptName: "機械工程",
    site: "https://mw.cycu.edu.tw"
  },
  {
    deptName: "化學工程",
    site: "https://che.cycu.edu.tw/"
  },
  {
    deptName: "醫學工程",
    site: "https://be.cycu.edu.tw/"
  },
  {
    deptName: "土木工程",
    site: "https://ce.cycu.edu.tw/wSite/mp?mp=4200"
  },
  {
    deptName: "環境工程",
    site: "https://bee.cycu.edu.tw/"
  }
];
let ulEl = document.querySelector("#depts");
```
- CSS
```css=
#depts li {
  font-size: 24px;
  
}
#depts li a {
  text-decoration: none;
  color: grey;
}
```
#### [createElement完整範例](https://codepen.io/augustting/pen/oNEEerx)
## 9、event (事件)
### 參考文件
[w3schools - HTML DOM Events](https://www.w3schools.com/jsref/dom_obj_event.asp)
### event物件-元素資訊
```htmlembedded=
<!DOCTYPE html>
<html>
    <body>
        <input type="button" value="Click" class="btn">
    </body>
</html>
```
```javascript=
let el  = document.querySelector('.btn');
el.onclick = function(e) {
    console.log(e);
    alert("CYCU");
}
```
### HTML元素綁定事件方式
1. 最古老的作法，在元素的屬性上直接給定事件
:::warning
不推薦使用，因為會有資安問題
:::
```htmlembedded=
<!DOCTYPE html>
<html>
    <body>
        <input type="button" value="Click" class="btn" onclick="hello()">
        <!--<input type="button" value="Click" class="btn" onclick="alert('CYCU')"> -->
    </body>
</html>
```
```javascript=
/*let el  = document.querySelector('.btn');
el.onclick = function(e) {
    console.log(e);
    alert("CYCU");
}*/
function hello() {
    alert("CYCU");
}
```
2. 在JavaScript來進行事件綁定
```htmlembedded=
<!DOCTYPE html>
<html>
    <body>
        <input type="button" value="Click" class="btn">
    </body>
</html>
```
```javascript=
// 方法 I
let el  = document.querySelector('.btn');
el.onclick = function(e) {
    console.log(e);
    alert("CYCU");
}
// 方法 II addEventListener(事件名稱, 事件處理器(函式), 捕獲或冒泡的切換) 
// false → event bubbling
// true → event capturing
// 捕獲或冒泡的切換，預設是false (冒泡)
//監聽
el.addEventListener('click',function(e){
  alert('hello');
},false)
```
### onclick and addEventListener差異
- `onclick`程式由上往下執行，事件會被底下的==覆蓋==掉
- `addEventListener`可以==疊加事件== 
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>
    <input type="button" class="btnSingle" value="single event">
    <input type="button" class="btnMulti" value="multiple event">
    
</body>
</html>
<script src="js/all.js"></script>
```

```javascript=

var singleEl = document.querySelector('.btnSingle');
singleEl.onclick = function (){
  alert('singile-1');
}
singleEl.onclick = function (){
  alert('singile-2');
}
// 事件監聽
var multiEl = document.querySelector('.btnMulti');
multiEl.addEventListener('click',function(){
  alert('Multi-1')
},false)
multiEl.addEventListener('click',function(){
  alert('Multi-2')
},false)

```
### 事件傳播：捕捉(Capturing)與冒泡(Bubbling)
- 冒泡 - 事件向外傳遞
- 捕捉 - 事件向內傳遞

```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="css/style.css">
    <title>Document</title>
</head>
<body class="body">
    <div class="box"></div>
</body>
</html>
<script src="js/all.js"></script>
```

```javascript=
var el = document.querySelector('.box');
el.addEventListener('click', function (e) {
	e.stopPropagation();
	alert('box');
	console.log(e);
});

var elBody = document.querySelector('.body');
elBody.addEventListener('click', function (e) {
	e.stopPropagation();
	alert('body');
	console.log('body');
});

```
### 中止事件傳遞`stopPropagation()`
### 取消預設觸發行為`preventDefault()`
### `change`事件應用
## 【補充】JavaScript 中 By Reference 和 By Value 的重要觀念
參考資料
- [談談 JavaScript 中 by reference 和 by value 的重要觀念](https://pjchender.blogspot.com/2016/03/javascriptby-referenceby-value.html)
- [深入探討 JavaScript 中的參數傳遞：call by value 還是 reference？](https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/) @ TechBridge by Huli
- [重新認識 JavaScript: Day 05 JavaScript 是「傳值」或「傳址」？](https://ithelp.ithome.com.tw/articles/10191057) @ iThome 鐵人賽 - 重新認識 JavaScript
### <i class="fa fa-square" aria-hidden="true"></i> By Value (類似於C語言中一般變數的使用)
當我們在建立 primitive type 的變數時（數字、字串、布林），假設我把a 指定成一個 primitive type 的時候，a 會在記憶體中存在一個自己的位置（假設叫做0x001）。這時候，當我指定另一個變數 b，它的值等同於 a 的時候，b 實際上會建立另一個獨立的記憶體位置（假設叫做0x002），接著再把 a 的值存在這個獨立的記憶體位置。也就是說， a 和 b 其實是**存在於兩個不同的記憶體位置**，因此彼此並不會乎相干擾影響，這種情況，我們就稱為==By Value== ，而這種情形會發生在 primitive type 的變數。
:::info
在 JavaScript 中 primitive type（Boolean, String, Number, null, undefined）都屬於 By Value。
:::
![](https://i.imgur.com/oiNuDQC.png)
> 圖片來源：[[Udemy] JavaScript: Understanding the Weird Parts](https://www.udemy.com/understand-javascript/)

### <i class="fa fa-square" aria-hidden="true"></i> By Reference (類似於C語言中的指標)
當我將變數 a 設立成一個Object（或function）時，這時候，一樣會在記憶體中給它一個位置（假設叫做0x001）；但是當我建立一個變數 b，並且把變數 b 的值等同於 a 時，這時候並不會再給它一個新的位置，而是一樣指定到物件 a 的位置（即0x001），實際上是不會有新的東西被建立，`變數 a 和 b 都會被指稱到相同的位置（即0x001）`，因此，當 a 的值改變的時候 b 的值也會改變，因為它們實際上是指稱到相同的位置，這種情形我們就稱為==By Reference==，這樣情況會發生在 Object 和 Function 這種變數。
:::info
在 JavaScript 中 Objects（Object, Array, Function）都屬於 By Reference。
:::
![](https://i.imgur.com/9hhFiL1.png)
> 圖片來源：[[Udemy] JavaScript: Understanding the Weird Parts](https://www.udemy.com/understand-javascript/)

### <i class="fa fa-square" aria-hidden="true"></i> By Sharing