# CH5 JS Basics
javascript最初創建的目的是為了**讓網頁活起來**,這種語言的程序稱為腳本(script)。javascript可以直接被寫在html,並在頁面加載時自動運行。
Vanilla JavaScript: 純javascript,而不需要任何而外的library(eg. jQuery)或框架。
每個瀏覽器有自己的js引擎,用來讀取並編譯js程式碼,若要確認某個瀏覽器的js引擎是否有支援某種功能,可參考 https://caniuse.com
## `<script>`放哪裡?
通常放在html的最下方,與CSS`<link>`不同,因為將js放在HTML正文的底部時,他會在任何js之前讓html, CSS有時間加載。`<script>`被放在HTML最下方所以是最後才會被加載。
先讓瀏覽器加載HTML, CSS,用戶不用等JS被解析完成,即可在網頁中看到某些內容,比較容易將使用者留下。
## 常見javascript函數
`console.log()` -- 將message輸出到控制台
`window.alert()` -- 提醒框
`window.prompt()` -- 有輸入格的提醒框
```javascript
let user_name=window.prompt("type your name");
window.alert("hihi" + user_name);
```
## Lexical Structure
自然語言(Natural Language):
1. 語法/文法grammar
2. vocab
程式語言(Programming Language)
1. 語法syntax
2. 保留字reserved word
Lexical Structure是一組基本規則(syntax)。
1. Case Sensitive: js的大小寫有差別
2. 空白、換行會被忽略: minification(刪除空白鍵、換行鍵)
3. 註解: ```// 單行註解```、`/*多行註解*/`
4. 變數名稱需要由文字、underscore`_`、dollar sign`$`當開頭
5. js內部有關鍵字(reserved word, keyword)不能當變數名稱
6. js使用Unicode字元集合,所以string內部可由任何Unicode文字組成
7. Semicolons`;`用來分隔程式語句,在js中可用可不用
## 變數與賦值
* 變數varible
* 賦值assignment
PS. 語法糖syntax sugar支援x=x+1更改為x+=1
### 宣告變數declare varible
1. 若數值的值會變動,使用`let`宣告
2. 若數值不會驗動,則使用`const`(constants常數)來宣告變數
3. 不要使用var宣告變數
需注意的規則:
1. 用const宣告變數,一定要馬上給予初始值(initializer)。若用let宣告變數但還沒賦值,則變數的值是`undefined`
2. 用const, let宣告的變數不能重複宣告(redeclaration)
3. const不能重複賦值(reassignment)
PS.JS引擎中有一個稱為garbage collector的後台程式,來監視所有物件並刪除那些變得無法訪問的object
## 數據(資料)類型data type
==7種基本數據類型(primitive data type)==
1. Number -- 整數與帶小數點的數字
2. BigInt -- 任意長度的整數
3. String -- 字符串
4. Boolean -- true, false
5. null -- 用來代表某個故意不存在的值
6. undefined -- 未被賦值的變數
7. symbol -- unique identifier
PS.第八種數據類型為object,屬於non-primitive data type,可能為array, object, function...
### Number
允許精確地表示介於2^(-253)和2^(253)之間的所有整數。如果使用大於此的整數值,可能會丟失數字的精度。
> JS處理有小數值的方式是採用double-precision 64bit binary format IEEE754 encoding
支援的運算符號: `+`,`-`,`*`,`/`,remainder(餘數) operator`%`(mod),exponentiation(指數) operator`**`,`++`,`--`,`+=`,`-=`,`/=`,`*=`
[x++, ++x差異](https://dev.to/yubo0826/x-x-chai-bie-yu-qi-zhong-de-han-yi-3e21)
### String
1. 在JS中可使用單引號或雙引號。
2. 兩個string的串接(concatenation)由`+`所完成的。
3. 不能做`-`,`*`,`/`的運算,若嘗試此類運算,因JS還是會嘗試算出某個值的結果,但兩個operand的值都不是數字,無法算出一個數字結果,結果會是`NaN`(Not a Number)。
4. `\n`換行
js是弱型別語言,會在背地裡將該變數轉換成可以被執行的資料型態,強行去執行。
```javascript
20+30+'string' //output: 50string
20+30+'string'+10+15 //output: 50string1015
```
### Number Methods
* `toString()` -- return一個數字的string
```javascript
//x is a number(x is an object)
let x = 27;
//對x來說,toString()是一個method or function
console.log(x.toString()+27); //output: 2727
console.log(typeof x); //number
```
* `toFixd(n)` -- return被轉換的數字,到小數第n位數
```javascript
let pi = 3.141592653589;
console.log(pi.toFixed(2)); //output: 3.14
console.log(typeof pi); //number
```
二進制不能精確地表示所有小數,這可能會導致意外結果,eg. `0.1+0.2 === 0.3` return false
#### String Attrubutes and Methods
[string](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/String)
* `length` -- return string長度(string的屬性)
* `[n]` -- return index第n項的字(index從0開始計算),`str.length-1`為最後一個字的index
* `slice(indexStart[, indexEnd])` -- 提取字串的一部分作為新的string返回,而不修改原始的字串,indexStart是inclusive(會被包含),indexEnd可不填,exclusive(不會被包含)
* `indexOf(substring)` -- return substring的第一個index位置,若找不到則return -1
* `toUpperCase()` -- return轉換為大寫的string,不會影響到string本身
* `toLowerCase()` -- return轉換為小寫的string,不會影響到string本身
* `splite(pattern)` -- return 透過搜索將一個字串以pattern分成一個有序的array,pattern可以是regular expression(正規表達式)
* `startsWith(s)` -- 確定string是否以指定字串s開頭,根據需要回傳true或false
* `endsWith(s)` -- 與`startsWith(s)`同,但確認結尾
* `includes(str)` -- return true如果string內部包含str
* `charCodeAt(n)` -- return int between 0 and 65535,表示給定index的UTF-16 code unit
常見編碼方式: ASCII
### Boolean
true, false
Unary(單一) operator`!`可以將boolean的值反轉
### Undefined & Null
#### undefined
1. 已宣告變數,卻沒有賦予initializer時,會出現undefined。
2. functions預設的return value為undefined。
#### Null
用來代表某個故意不存在的值
## JavaScript operators
運算元(operand)、運算符(operator),eg. a+b, `a` `b` are operands, `+` is an operator
* assignment operator
* comparison operator
* logical operator
* typeof operator(unary)
* negation operator `!`
* increment operator `x++`, `x--`
* bitwise operator
* srithmetic operator `+`,`+=`,`-=`, ...
### Comparison Operators
js的comparuison operators的運算元是兩個任意資料型態,運算結果為boolean值
* `==` -- return true if operands are equal
* `!=`
* `===` -- return true if operands are equal and of the same data type
* `!==`
* `>`, ...
### Logical Operators
operands為任意資料型態,且運算結果為boolean

### Bitwise Operators
在decimal系統中,可用的數字集合是`{0,1,2,3,4,5,6,7,8,9}`,由於在9之後就沒有數字可用了,因此使用2位數來標記下一個數字。
在binary系統中,可用的數字集合是`{0,1}`,在1之後則需要進位來代表下一個數字 => 0,1,10,11,100,101,110,111,...
|binary |0|1|10|11|100|101|110|111|1000|
|-------|-|-|- |- | - | - | - | - | -- |
|decimal|0|1|2 |3 | 4 | 5 | 6 | 7 | 8 |
若要把二進制的數字轉換成十進制,公式為: (數學歸納法)

[binary to decimal](https://www.rapidtables.com/convert/number/binary-to-decimal.html)
js的bitwise(每兩個bit) operators將數字operand視為32bits的數字(binary),並且對每個bit進行運算。
bit -- binary digit
`
let a = 10, b = 9;`
* `a&b` -- 在兩個operand的對應位都是1的位置返回一個1 (AND)
```javascript
console.log(a&b); // 8
a=1010
b=1001
> 1000
```
* `a|b` -- 在兩個operand的對應位都是0的位置返回一個0 (OR)
```javascript
console.log(a&b); // 11
a=1010
b=1001
> 1011
```
* `a^b` -- 在兩個operand的對應位相同的每個位置返回0 (XOR)
```javascript
console.log(a&b); // 3
a=1010
b=1001
> 0011
```
* `~a` -- 反轉operand的每個bit
```javascript
console.log(~a); // 5, js顯示-11是因為tools component?的問題(學CPU才會知道,可忽略)
a=1010
> 0101
```
* `a<<b` -- 將二進制表示中的a向**左**移動b位制,丟棄任何被移出的bit
```javascript
console.log(a<<1); //20
a=1010 所有都向左移動一格,右邊補0
> 10100
```
* `a>>b` -- 將二進制表示中的a向**右**移動b位制,丟棄任何被移出的bit
```javascript
console.log(a>>1); //5
a=1010 所有都向右移動一格,最後的0丟棄
> 101
```
#### 何時會用到Bitwise Operators?
:::info
* 編碼運算
* 資料傳出,sockets programming, ports
* 資料加密、SHA函數
* 作業系統、CPU
* Finite State Machine
* Graphics(eg. 影像處理、人工智能)
:::
## if statement
if是最簡單的決策語句
`if(condition)statement;`
```javascript
if(condition 1){
statement 1;
}esle if(condition 2){
statement 2;
}esle{
statement 3;
}
```
### Tyuthy and Falsy Values
在javaScript中,每個值(data type)在boolean context之下,都可以被視為true或false。在boolean context之下,js會自動做type coercion,常見的boolean context:
1. if statement
2. logical operators運算
3. others
js中的Falsy Values:
* false
* 0, -0, 0n(BitInt)
* "", '', `` ("".length=0)
* null
* undefined
* NaN
除此之外的都是Truthy Values,包含[] empty array, {} empty object...等
## ==Coding Convention(習慣) and Restrictions==
### coding convention:
1. 變數與函數的名稱全部小寫,若由2個以上的單字組成,則使用camelCase或用underline
[常見重點整理 - 命名慣例 & 開發時注意事項](https://hackmd.io/@Heidi-Liu/note-common)
2. operators前後加空白鍵
3. 用分號結束一個簡單的statement
4. Constants都使用大寫
5. Class由大寫字母開頭
### coding restrictions:
1. 變數、函數名稱不可由數字開頭
2. 變數、函數名稱不可包含Hyphen`-`
3. 變數、函數名稱不可使用reserved words
# CH6 JS Basics 2
## function 函式(method 方法)
### 數學函數
函數應該只返回一個值
`f(x)=3x+6`,x為參數(parameter)
`f(5)`,5為自變數(argument)
### javascript function
[Functions](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Functions)
declare a function
```javascript
// declaration, definition
function FuncName(para){
statements
return result;
}
//function exection, Invoke a function, function invocation, call a function
//調用function
FuncName();
```
若無return語句,則return undefined(js的function默認的return value)
```javascript
undefined + undefined //NaN
```
```javascript
function convertor(c){
return c * 1.8 + 32;
}
let input = Number(prompt("請輸入溫度(C)"));
let result = convertor(input);
alert("換算後的溫度為" + result + "F");
```
每個js function實際上都是一個object(有instance properties, instance methods)
## Array 陣列
array不是primitive data type,可儲存用途或性質相近的資料
`let arr = []`
1. js array可以調整大小,且可包含不同資料型態 `srr = [1, 'A']`
2. js array中的元素必須使用非負整數作為index
3. js array的第一個元素為0,最後一個為array.length-1
4. js array會複製reference
```javascript
let friends = ['gracr', 'mike', 'spencer'];
let anotherVar = friends; // 複製reference
anotherVar[0] = 'michael';
console.log('friends arr: ');
console.log(friends) // 'michael', 'mike', 'spencer'
console.log('anotherVar arr: ');
console.log(anotherVar); // 'michael', 'mike', 'spencer'
```
### Array Instance Properties
* `.length`
### 常見的Array Instance Methods
* `push(element)` -- 將一個或多個元素添加到array的末尾,並return array的新長度
* `pop()` -- 從array中刪除最後一個元素並return該刪除的元素
* `shift()` -- 從array中刪除第一個元素並return該刪除的元素
* `unshift(element)` -- 將一個或多個元素添加到array的開頭,並return array的新長度
當array內部的元素還有array時,稱為array of arrays
```javascript
let myArr = [['name', 'address', 'age'], ['mike', 'taiwan', 35], ['john', 'us', 26]];
console.log(myArr[1][0]); // mike
```
## <補充>==primitive data type vs. reference data type==
> RAM(Random-access memory, 記憶體)
### primitive data type
copy by value
```javascript
let deposit = 500; //在RAM找到一個位置存取deposit=500
let anotherDeposit = deposit; //找到另一個位置存取anotherDeposit
anotherDeposit = 600; // anotherDeposit = 600
console.log('deposit: ' + deposit + ', anotherDeposit: '+anotherDeposit);
// deposit: 500, anotherDeposit: 600
```
### reference data type
copy by reference,for 減少記憶體占用, OOP(物件導向程式設計)
```javascript
let friends = ['a', 'b', 'c']; // 將friends指向a, b, c的位置
let another = friends; // 將friensds的位置給another
another[0] = 'd'; // 將'a'位置的值改成'd'
```
### `==` 差異
```javascript
let x = 10;
let y = 10;
// x, y各指向不同的RAM位置且值=10
// 因為x, y是primitive data type,因此比較時js會比較指向RAM位置的值
console.log(x == y); // true
```
```javascript
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
// arr1, arr2分別指向RAM不同位置的[1, 2, 3]
// 因為arr1, arr2是reference data type,因此比較時js會比較指向的RAM位置
console.log(arr1 == arr2); // false
```
## Function的時間複雜度
在CS中演算法的時間複雜度(Time complexity)可以描述該演算法執行時間,常用`O`(Big O Notation)表示,不包括函式的低階項和首項係數。
* 橫軸(x)為參數大小,縱軸(y)為執行時間
* `f(n)=5n^3 + 3n` -> 不包括函式的低階項(`3n`)和首項係數(`5`) -> 時間複雜度=`O(n^3)`
為了計算時間複雜度,通常會估計演算法的操作單元數量,每個單元執行的時間都是相同的。
時間複雜度: 在參數逐漸變大的形況下,function執行完畢所需要的時間的成長速度。
### Function的時間複雜度
js陣列常見Method的時間複雜度:
* `push` -- O(1) (參數的成長和所需完成的時間無關)
* `pop` -- O(1)
* `shift` -- O(n) (把前面的元素去掉,後面所有的元素往前移一格)
* `unshift` -- O(n)
每個js引擎內部對於陣列的實現方法略有差異,對於不同大小的陣列,可能會使用其他更好的資料結構,eg. double-linked list, binary search tree(BST)。
### Big O Notation Formal Definition

## Object 物件
1. 每個js的物件都有properties, method
2. 屬於物件的function被稱為method
3. 物件的屬性與相對應的值是一種key-value repair
4. 獲取物件的方式可以透過dot notation或`[]`
5. js object是一種hashtable雜湊表(O(1))
```javascript
let obj = {
first_name: "Wilson",
last_name: "Ren",
age: 26,
is_married: true,
spouse: "Grace",
sayHi() {
console.log(this.first_name + " says Hi");
},
walk() {
console.log(this.first_name + " is walking...");
},
speak(words) {
console.log(this.first_name + " says " + words);
},
};
console.log(obj.first_name); // dot notation
console.log(obj["first_name"]); // []
obj.sayHi();
obj.speak("Happy");
```
6. `this`指的是調用該方法的物件,若某個function沒有調用該function的物件,則`this`是指向global物件(在瀏覽器中指的是window物件)
```javascript
function hello() {
console.log("hello");
console.log(this);
}
hello();
```
7. 在js中function, array都是object (special type of objects)
```javascript
function hello() {
console.log("hello");
}
console.log(typeof hello); // function
console.log(typeof [1, 2, 3]); // object -> bug
```
[Array.isArray()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
## Loop 迴圈
重複做某件事,`while loop`, `do while loop`, `for loop`,若把`return`放到loop內部,循環會馬上停止。
### for loop
1. initialization: declare循環開始前的計數器變數
2. final expression: 每次循環迭代結束時要執行的程式碼,一般用於更新或遞增計數器變數
```javascript
// initialization: declare循環開始前的計數器變數
for(initialization; condition; final expression){
...
};
```
### while loop
若忘記加計數器而導致while loop無限循環,電腦CPU可能會癱瘓
* 適合不知道loop要執行幾次的時候
```javascript
while (condition){
...
};
```
### do while loop
該循環指定的statement,直到條件=false
* 先執行語句後再評估條件
```javascript
do{
...
} while();
```
### Nested Loop 巢狀迴圈
在loop內部還有另一個loop的情況
### break, continue
* `break` -- 終止迴圈,如果在Nested Loop中使用,只會終止使用包含`break`的loop,若需要終止Nested loop,則需要使用`return`
* `continue` -- 跳過`continue`之後的循環內code,強制執行循環的下一次迭代
### loop跑array
```javascript
let arr = ["Mike", "Grace", "Json"];
for(let i = 0; i < arr.length; i++){
console.log(arr[i] + " is my friend.");
}
```
## 練習
### 終極密碼
```javascript
let ans, startNum, endNum, isFlag = true;
function init() {
// 0 <= Math.random < 1
// 0 <= Math.floor(Math.random * 100) < 1
ans = Math.floor(Math.random() * 100);
startNum = 0;
endNum = 99;
}
while (isFlag) {
init();
while (true) {
let yourNum = prompt("請輸入你的猜測(" + startNum.toString() + "~" + endNum.toString() + ")");
if (yourNum < startNum || yourNum > endNum) {
alert("無效猜測,請重新猜測一個數字");
continue;
}
if (yourNum == ans) {
isFlag = confirm("猜對了,秘密數字是" + yourNum + "! 還要繼續嗎?");
break;
} else if (yourNum < ans) {
startNum = yourNum;
} else {
endNum = yourNum;
}
}
}
```
### 反轉陣列
將一個array內部的所有元素反過來!
(禁止使用Array.prototype.reverse()。另外,考慮時間複雜度,需要在O(n)內,不能是O(n^2))
例如:
`const friends = ["Harry", "Ron", "Snap", "Mike", "Grace"];`
會變成
`["Grace", "Mike", "Snap","Ron","Harry"];`
```javascript
const friends = ["Harry", "Ron", "Snap", "Mike", "Grace"];
let a = 0, b = friends.length - 1;
while (a < b) {
let tmp;
tmp = friends[a];
friends[a] = friends[b];
friends[b] = tmp;
a++;
b--;
}
console.log(friends)
```
```javascript
const friends = ["Harry", "Ron", "Snap", "Mike", "Grace"];
const reversed_friends = [];
for (let i = friends.length - 1; i >= 0; i--) {
reversed_friends.push(friends[i]);
}
```
### 找出最大值
在這個coding練習裡面,你要寫一個函式,他可以接受一個parameter,參數是一個array of numbers。這個函式要return一個值,也就是Array當中最大的數! 若是 input是一個 empty array,則return undefined。例如:
```javascript
findBiggest([15, 20, 22, 16, 7]); // return 22
findBiggest([1, 2, 3, 4, 5, 999]); // return 999
findBiggest([-11, 0, -3, -4, -5, -999]); // return 0
findBiggest([]); // return undefined
```
```javascript
function findBiggest(arr) {
console.log('start:' + arr)
if (arr.length == 0) {
return undefined;
}
let max = arr[0];
for (let i = 0; i < arr.length; i++) {
console.log("compare(max, arr[i]): " + "(" + max + "," + arr[i] + ")")
if (max < arr[i]) {
console.log('change: max' + arr[i])
max = arr[i];
}
}
return max;
}
```
### 數值加總
addUpTo() 這個函式有大於0的參數 n,n為一個正整數。 addUpTo() 要 return 1 + 2 + 3 + ..... + n 的值。
例如:
```javascript
// addUpTo(5) = 1 + 2 + 3 + 4+ 5 = 15
// addUpTo(100) = 1 + 2 + 3 + 4 + 5 + ....... + 100 = 5050
addUpTo(5); // 15
addUpTo(100); // 5050
addUpTo(5000); // 12502500
addUpTo(100000); // 5000050000
```
```javascript
function addUpTo(num) {
let sum = 0;
for (let i = 1; i <= num; i++) {
sum += i;
}
return sum;
}
```
# 程式練習題
[題目連結](https://www.notion.so/yuhsien/179caa9730284da48b0a6df66886a447)
# CH7 Document Object Model (DOM)
## DOM & Window Object
文件物件模型(DOM)是HTML的程式介面,允許在JS當中操作HTML元素。
* DOM提供了一個文件的結構化(Tree)表示法,讓程式可以存取並改變文件架構、風和、內容。
* DOM提供了文件已擁有屬性與函式的節點與物件組成的結構化表示。
* 節點可以附加事件處理程序,一旦樹發事件就會執行處理程序。
* 本質上,他將網頁與腳本或程式語言連在一起。
### Window Object
`this` -- 1. JS物件method 2. window object
`window.alert()` -- alert function is a method of window object
`window.prompt()`
`window.addEventListen()` -- 將事件監聽程式碼附加到window object
`window.setInterval()` -- 每次經過給定的毫秒數時安排一個函數執行
`window.clearInterval()` -- 將`setInterval()`所重複執行的程式暫停
> window object用的時候可以省略window
```javascript
function sayHelloToUser() {
alert("3秒過了...")
}
let interval = window.setInterval(sayHelloToUser, 30000);
window.clearInterval(interval);
```
> 物件導向程式概念: 一個object可以是另一個object的attribute。
```javascript
let Grace = {
name: "Grace",
age: 20
};
let Wilson = {
name: "Wilson",
age: 25,
spouse: Grace
};
console.log(Wilson.spouse.age); // 20
```
Window Object可使用的常見properties:
* `window.console` -- retun一個concole object,可以對瀏覽器的debugging console進行控制與訪問。常用的method為`log()`, `error()`
* `window.document` -- return window包含的文檔(HTML document object),也就是html文件
* `window.localStorage` -- return一個local stage物件
* `window.sessionStorage` -- return一個session stage物件
### Document Object Model
從DOM可知HTML也被視為是個物件。這個架構被稱之為是Model。
1. doucument是一個object,是window object的一個屬性
2. doucument是指HTML doucument
3. 這個模型意味著所有doucument內部的HTML元素都是object (每個HTM tag都有其屬性和方法)
每個點都是一個節點(node)

## get element by id or class
在DOM這棵樹上,每個點被稱為節點(nodes),分為三種:
1. HTML元素節點(element nodes or element object)
2. 文字節點(text node)
3. 註解節點(comment node)
DOM提供2種節點集合(not array):
1. HTML collection (only element nodes)
2. NodeList (所有3種nodes)
## Document object
Document object指的是`window.document`下的屬性,常見的method:
> 注意回傳的data type
* `window.document.addEventListener()`
* `window.document.createElement(tagName)`
* `window.document.getElementById(id)` -- return第一個id相符合的element object
* `window.document.getElementByClassName(className)` -- return一個動態的HTML collection,內部元素包含所有給定className的元素
* `querySelector(selectors)` -- return document第一個符合特定選擇器群組的element object,採用深度優先搜尋演算法
* 深度優先搜尋演算法: 走得越深越好,逐一走過每個tag去尋找
* `querySelectorAll(selectors)` -- return一個靜態(not live) NodeList,表示與指定選擇器組匹配的元素列表
> querySelector在現在的專案中比較常使用,使用CSS的selector
> CSS selector: id - `#id`, class - `.class`
### HTML collection & NodeList
注意: HTML collection, NodeList are not array (cannot use `pop`, ...)
#### HTML collection
是一種array-like object
[HTMLCollection](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) -- 動態 dynamtic,會因為DOM改變而length會跟著改變
#### NodeList
[NodeList](https://developer.mozilla.org/zh-TW/docs/Web/API/NodeList) -- 靜態 static,不會因為DOM改變而length會跟著改變,改變DOM之後再重新取得一次
### 差別比較
#### ==childNodes & children==
Element Object是三種節點的其中一種
* 每個節點Node有`childNodes`屬性,return type為NodeList,包含了此節點在DOM Tree之下的第一層的所有節點
* 每個Element Object有`children`屬性,return type為HTML collection,包含了此節點在DOM Tree之下的第一層的所有HTML Object
Element Object這種node可以使用`childNodes`&`children`屬性,但另外兩種nodes(text, comment)只有能夠使用`childNodes`,若使用`children`則return `undefined`。
#### JS取得html元素方法
| Methods | return type |
| ---------------------------- |:---------------------------------------- |
| getElementById(id) | Element object |
| getElementByClassName(class) | HTMLCollection(內部元素為Element object) |
| querySelect(selector) | Element object |
| querySelectAll(selector) | NodeList(內部元素為Nodes) |
#### NodeList vs HTMLCollection

```javascript
let hellos = document.querySelectorAll(".hello");
hellos.forEach((hello) => {
console.log(hello);
})
let helloss = document.getElementsByClassName("hello");
helloss.forEach((hello) => {
console.log(hello); // Uncaught TypeError: helloss.forEach is not a function
})
```
## Function Declaration & Expression
在JS中定義function有兩種方式:
* Declaration -- `function funcName (para){}`
* Expression -- `function (para){}`,省略function name來創建匿名函數
### Function Expression用途
* 定義沒有名稱的函數放入變數中,讓程式碼更有彈性。PS.與Declaration幾乎沒有區別,但若使用Declaration,可在定義函數前使用該function
```javascript
let Wilson = {
name: "Wilson",
greet() {
console.log(this.name + "打招呼")
},
walk: function () {
console.log(this.name + "正在走路")
}
};
console.log(Wilson.walk()); //Function Expression
console.log(Wilson.greet());
```
* 作為higher order function的callback function使用,ex. forEach, addEventListener
> higher order function: 在function中放入function,A(B),A: higher order function
優點:
1. 若程式碼有許多callback function都是使用function declaration,命名變數時都需要避開function declaration
2. callback function名稱其實沒有意義
3. 程式碼變乾淨
```javascript
function react() {
alert("有人正在點擊畫面");
}
// parameter 1: 是哪個event
// parameter 2: 事件發生後,我們要執行的function
// addEventListener 本身是一個higher order function
// react是一個callback function
window.addEventListener("click", react);
// Function Expression
window.addEventListener("click", function () {
alert("有人正在點擊畫面");
});
```
* 使用IIFE(Immediately Involied Function Expression)的功能
在伺服器的程式碼可以使用(打開後就立即要執行)
```javascript
(function (a, b) {
console.log(a + b);
})(10, 5);
```
### Arrow Function Expression
JS中的函數是first-class object(一等公民),可以:
1. 將function分配給變數: `let hello = function(){};`
2. 將function當作argument傳給其他function(=數學的composite function)。higher order function的argument被稱為callback function。
在許多情況下,若要把function當作argument傳給其他function,那麼對此函數命名則變的沒有意義,建議使用function expression。JA中Arrow Function Expression是function expression的簡化語法。
* `() => ...`
```javascript
() => {
console.log("hello");
}
let Wilson = {
name: "Wilson",
greet() {
console.log(this.name + "打招呼")
},
// arrow function express沒有this keyword綁定
walk: () => {
console.log(this); // {} 空物件
console.log(this.name); // undefined
console.log("Wilson is walking....");
}
};
```
* `param => ...`
* `(param1, param2, ...) => ...`
#### 總結
1. 若有一個參數,則不需要加括號;若有0或2 up的參數,則需要加括號
2. ...(expression)無加大括號(curly brackets),會自動return (only一個計算式)
```javascript
let num1 = () => 15; // 15
let num2 = () => { 15 }; // undefined
let num3 = () => { return 15 }; // 15
let addition1 = (a, b) => a + b;
console.log(addition1(10, 30)); // 30
let addition2 = (a, b) => {
let pi = 3.14;
return a * b * pi;
};
console.log(addition2(10, 30)); // 30
```
3. **Arrow Function Expression沒有this關鍵字綁定,不應該用作objects的method**
### forEach() Method
forEach()為higher order function,與arrow function expression協作為:
* `forEach(element => ...)`
* `forEach(callbackFn)`
* `forEach((element, index) => ...)`
```javascript
let myLuckyNumbers = [1, 2, 3, 4, 5, 6, 7];
// 1. for迴圈
for (let i = 0; i < myLuckyNumbers.length; i++) {
console(myLuckyNumbers[i] + 3);
}
2. 使用funciton declaration
function plus3(n) {
console(n + 3);
}
myLuckyNumbers.forEach(plus3);
// 3. 使用function expression創建匿名函數
myLuckyNumbers.forEach(function (n) {
console(n + 3);
});
// 4. arrow function expression
myLuckyNumbers.forEach((n) => {
console(n + 3);
});
// 搭配index
myLuckyNumbers.forEach((n, index) => {
console(n + " is at index " + index);
});
```
## Element objects
在DOM Tree中,每個HTML元素可能有自己獨特的properties和methods,eg. `<video>`, `<h1>`...。
除了各自的properties, methods外,也有共用的properties, methods:
* `addEventListener(event, callbackFn)`
```javascript
let myBtn = document.querySelector("#my-btn");
myBtn.addEventListener("click", () => {
alert("你點了btn");
})
```
* `appendChild(element)`
```javascript
let body = document.querySelector("body");
let myH1 = document.createElement("h1");
body.appendChild(myH1);
```
* `innerHTML`
* `innerText`
```javascript
let body = document.querySelector("body");
let myH1 = document.createElement("h1");
//innerHTML: 會被當作HTML的程式理解,可以插入tag, innerText: 只會被當作文字
myH1.innerText = "<a>這是我的h1</a>";
myH1.innerHTML = "<a href='https://www.google.com'>這是我的h1</a>";
body.appendChild(myH1);
```
* `children` -- return HTMLcollection
* `childNodes` -- return NodeList
* `parentElement` -- 選取父元素
* `classList` -- element class的列表,可以用的method有`add()`, `remove()`, `toggle()`, `contains()`
* `toggle()` -- 有屬性的話刪除,沒有的話則加入
* `contains()` -- 檢查是否有指定的class
* `getAttribute(attributeName)` -- 獲得一個屬性,eg. href
```javascript
<a title="到google首頁" href="https://www.google.com"></a>
let a = document.querySelector("a");
a.getAttribute("title");
a.getAttribute("href");
```
* `querySelector(selector)` -- document object也可以用,但element object使用只找element底下的
* `querySelectorAll(selector)` -- document object也可以用,但element object使用只找element底下的
* `remove()` -- 直接移除此tag (not hidden)
```javascript
<button>讓anchor tag消失</button>
<a title="到google首頁" href="https://www.google.com"></a>
let button = document.querySelector("button");
button.addEventListener("click", ()=>{
let a = document.querySelector("a");
a.remove();
})
```
* `style` -- 可以用來改變element object的inline style,因為js中不能使用hyphen`-`,因此JS中的CSS屬性都被更改為cameCase
```javascript
let button = document.querySelector("button");
button.style.backgroundColor = "green";
button.style.color = "white";
button.style = "background-color: green; color: white;";
```
## Inheritance 繼承
可以將attribute, methods從一個class繼承到另一個class。
* subclass(子類), child class -- 從一個class繼承的class
* superclass(父類) -- 繼承自的class
> object B -> object A: B為subclass, A為superclass
優點:
1. 減少code, RAM下降, 提升速度
2. 較好維護
所有的html元素都從element object繼承了peoperties, methods,其中某些HTML元素有自己獨特的peoperties, methods。

## JavaScript Events
表示一個在DOM物件所發生的事件,事件通常由使用者的操作行為所產生(ex: 調整螢幕大小、點滑鼠、敲鍵盤...)。
`addEventListener(type, listener)` - 等待事件發生,去執行被賦予的func。
* `type`: 事件類型,eg. click, resize...
* `listener`: callback function,被執行時,js會把event object (`e`)當作argument,放進listener執行
```javascript
let button = document.querySelector("button");
button.addEventListener("click", (e)=>{
console.log(e);
})
```
### JS Event繼承關係

[Event reference list MDN](https://developer.mozilla.org/zh-TW/docs/Web/Events#%E4%BA%8B%E4%BB%B6%E5%88%86%E9%A1%9E)
```javascript
window.addEventListener("keydown", (e) => {
console.log(e); // keycode: 按的鍵
})
```
### event object常用屬性、方法
* `target` -- 指向最初觸發事件的DOM物件
* `preventDefault()` -- 取消事件的預設行為,但部會影響事件的傳遞,事件仍會繼續傳遞
* `stopPropagation()` -- 可以防止event bubbling進一步傳播當前事件
```javascript
<input type="text" />
<input type="number" min="0" max="10" />
<button>交出表單</button>
let button = document.querySelector("button");
button.addEventListener("submit", (e) => {
e.preventDefault(); // 取消交出表單的預設行為
})
```
## Event Bubbling 冒泡事件
當一個事件發生在一個元素上時,他首先在其上運其event handler,然後運行其parent element的event handler,然後一職網上運行其他祖先的event handler。
> In programming, an event handler is a callback routine that operates asynchronously once an event takes place.
```javascript
<style>
.a {
width: 300px;
height: 300px;
background-color: red;
}
.b {
width: 150px;
height: 150px;
background-color: blue;
}
</style>
<div class="a">
<div class="b"></div>
</div>
let a = document.querySelector("a");
let b = document.querySelector("b");
a.addEventListener("click", () => {
alert("紅色框的事件監聽器正在被執行");
})
b.addEventListener("click", () => {
e.stopPropagation(); // 防止冒泡事件
alert("藍色框的事件監聽器正在被執行");
})
```
## Local Storage & Session Storage
Storage是瀏覽器儲存數據的地方,並非database。在Storage內部儲存的數值都是key-value pair,且key, value都會被轉為string。
* Local Storage: 沒有過期的時效
* Session Storage: 用戶關閉瀏覽器後,sessionStorage就會被銷毀
localStorage和sessionStorage使用的methods是一樣的:
* `setItem(key, value)` -- 當傳遞一個key和value時,會將該key-value pair加到給定的storage,或是該key的值已存在,則更新key的value
* `getItem(key)` -- 給定key取得value,若值不存在,則return `null`
```javascript
localStorage.setItem("name", "wilson");
localStorage.setItem("age", 26);
let myAge = localStorage.getItem("age");
console.log(myAge);
```
* `removeItem(key)` -- 從給定的storage中刪除該key-value pair
* `clear()` -- 清除儲存在給定storage中的所有key-value pair
Storage bind 網址

### JSON
JSON(JavaScript Object Notation),是一種交換資料的格式。JSON Object有兩個method:
* `JSON.stringify(value)` -- 將value換成JSON String
* `JSON.parse(text)` -- 解析JSON String,製作出JSON String描述的JavaScript值或是Object
array -> JSON.stringify => JSON String
JSON String -> JSON.parse => array

```javascript
let myLuckyNumbers = [1, 2, 3];
localStorage.setItem("myLuckyNumbers", JSON.stringify(myLuckyNumbers));
let myNumber = JSON.parse(localStorage.getItem("myLuckyNumbers"));
console.log(typeof myNumber);
myNumber.forEach(n => {
console.log(n)
});
```