# 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

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

```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}`);
```

出處:[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文件

##### 轉換成DOM

### 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。
:::

> 圖片來源:[[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。
:::

> 圖片來源:[[Udemy] JavaScript: Understanding the Weird Parts](https://www.udemy.com/understand-javascript/)
### <i class="fa fa-square" aria-hidden="true"></i> By Sharing