# 📚 前端網頁開發基礎(JavaScript) - 學習筆記
> [!Note]
> **播放清單連結:** [點此前往](https://youtube.com/playlist?list=PL-g0fdC5RMbqW54tWQPIVbhyl_Ky6a2VI&si=R7yjmSfWzsqPtw7e)
> **學習總覽:** 雖然已經學過了基礎的HTML、CSS、Javascript用來做一個網站,但是都是依靠AI來完成,希望藉由這個播放清單來複習一下。
---
## 🚀 學習進度
- [x] **影片 21:** JavaScript Array 陣列物件
- [x] **影片 22:** JavaScript HTML DOM 核心觀念
- [x] **影片 23:** JavaScript HTML DOM 網頁畫面操作演練
- [x] **影片 24:** Javascript Event Handling 事件處理
- [x] **影片 25:** HTTP 通訊協定簡介
- [x] **影片 26:** AJAX 網路連線實務
- [x] **影片 27:** JavaScript 箭頭函式 Arrow Function
- [x] **影片 28:** JavaScript 解構賦值 Destructuring Assignment
---
## 🎬 影片 21: JavaScript Array 陣列物件
### 🎯 本集重點 (Key Takeaways)
* **重點一:陣列 (Array) 是「有序的資料集合」**
* 陣列是一個特殊的物件,專門用來存放一長串有順序的資料。它可以儲存任何型態的資料,包括數字、字串、布林值,甚至是其他物件或陣列。
* **重點二:使用 `[]` (陣列字面值) 來建立陣列**
* 在現代 JavaScript 中,使用中括號 `[]` 是建立陣列最常用、也最推薦的方式。例如:`let scores = [100, 98, 85];`。
* **重點三:索引 (Index) 是從 `0` 開始的**
* 陣列中的每個資料都有一個從 `0` 開始的編號(索引)。要取得第一個資料,我們使用 `array[0]`,第二個則是 `array[1]`,依此類推。
* **重點四:`.length` 屬性與 `.push()` 方法**
* **`.length`** 屬性可以告訴我們陣列中「總共有幾個資料」。
* **`.push(資料)`** 方法可以將一筆新資料「新增到陣列的結尾」。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **1. 什麼是陣列 (Array)?**
* **比喻**:如果說物件 (`{}`) 像是一個有「具名標籤」的檔案櫃,那麼陣列 (`[]`) 就像是一列有「編號車廂」的火車。它專門用來存放**有順序性**的資料。
* 陣列中的每個位置都有一個固定的編號,這個編號就稱為**索引 (Index)**。
---
* **2. 建立陣列**
* **方法一:陣列字面值 `[]` (Array Literal) - 推薦**
* 這是建立陣列最簡單、直接且最常用的方式。
* **建立空陣列**:
```javascript
let arr = []; // 建立一個空的陣列
```
* **建立時同時放入資料**:
```javascript
// 每個資料用逗號隔開
let grades = [70, 50, 84];
```
* **方法二:`new Array()` 建構式 (較少用)**
* 這是另一種建立陣列的語法,在某些特殊情境下會用到,但一般情況下建議使用 `[]` 即可。
```javascript
let arr = new Array(); // 等同於 let arr = [];
```
---
* **3. 操作陣列中的資料**
* **A. 新增資料到結尾 (`.push()`)**
* `.push()` 是陣列物件內建的一個「方法」(功能),可以把新資料塞到陣列的最後面。
* **語法**:
```javascript
let arr = [];
arr.push(3); // arr 現在是 [3]
arr.push(4); // arr 現在是 [3, 4]
```

* **B. 根據索引取得資料**
* 陣列的索引是從 `0` 開始計算的,這點非常重要!
* **語法**:`陣列[索引]`
* **範例**:
```javascript
let arr = [3, 4];
console.log(arr[0]); // 印出第一個資料:3
console.log(arr[1]); // 印出第二個資料:4
```
* **C. 取得陣列長度 (`.length`)**
* `.length` 是陣列物件的一個「屬性」,它會告訴我們這個陣列裡目前有幾筆資料。
* **範例**:
```javascript
let arr = [3, 4, 10, 25];
console.log(arr.length); // 印出 4
```
---
* **4. 陣列的最佳拍檔:`for` 迴圈**
* 陣列的「有序性」和「長度 (`.length`)」,讓它和 `for` 迴圈成為處理大量資料的最佳組合。我們可以透過迴圈,逐一取出陣列中的每一筆資料來進行運算。
* **標準遍歷寫法**:
```javascript
let scores = [70, 50, 84, 25, 100];
// i 從 0 開始,直到 i 小於 scores 的長度為止
for (let i = 0; i < scores.length; i++) {
// scores[i] 會依序取出 scores[0], scores[1], ...
console.log("第 " + (i+1) + " 個人的分數是:" + scores[i]);
}
```
---
* **5. 綜合練習**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Javascript:陣列物件</title>
</head>
<body>
<script>
// === 練習 1:手動建立與操作陣列 ===
let grades = []; // 建立空陣列
console.log("初始空陣列:", grades);
grades.push(70);
grades.push(50);
grades.push(84);
console.log("push 後的陣列:", grades);
console.log("陣列長度:", grades.length); // 印出 3
// === 練習 2:建立時即包含資料 ===
let grades2 = [70, 50, 84];
grades2.push(25);
grades2.push(100);
console.log("grades2 陣列:", grades2);
// 透過索引取得資料
console.log("grades2 的第一個元素:", grades2[0]); // 70
console.log("grades2 的第五個元素:", grades2[4]); // 100
console.log("--- 使用迴圈計算總分與平均 ---");
// === 練習 3:利用 for 迴圈,計算 grades2 的總和與平均 ===
let sum = 0; // 用來記錄累加的結果
for(let i = 0; i < grades2.length; i++){
// sum = sum + grades2[i] 的簡寫
sum += grades2[i];
}
console.log("grades2 的總分是:", sum);
// 計算平均值
// let avg = total / grades.length; // 這是錯誤的邏輯!變數用錯了。
let avg = sum / grades2.length; // 正確邏輯:應使用 grades2 的總和(sum) 除以其自身的長度
console.log("grades2 的平均分數是:", avg);
</script>
</body>
</html>
```
:::spoiler 成果展示

:::
---
## 🎬 影片 22: JavaScript HTML DOM 核心觀念
### 🎯 本集重點 (Key Takeaways)
* **重點一:DOM 是 HTML 文件的「物件模型」**
* DOM (Document Object Model) 是瀏覽器根據 HTML 原始碼,在記憶體中建立的一個樹狀結構。每一個 HTML 標籤,都會被轉換成一個 JavaScript 可以操作的「物件」。
* **重點二:JS 透過 DOM 來操控網頁**
* JavaScript 本身無法直接「看到」網頁,它必須透過 DOM 這個統一的「介面」,來讀取、新增、修改或刪除網頁上的任何內容與樣式。
* **重點三:`window` 是最頂層的全域物件**
* `window` 物件代表整個瀏覽器視窗,是所有客戶端 JavaScript 功能的根源。所有全域變數、函式和物件(如 `document`、`screen`)實際上都是 `window` 的屬性。
* **重點四:`document` 是網頁內容的入口**
* `document` 物件代表當前載入的整個 HTML 文件,是我們用來選取和操作頁面上具體元素的起點。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **1. 什麼是 HTML DOM?**
* **比喻**:
* **HTML 原始碼**:像是一棟房子的「設計藍圖」。
* **瀏覽器**:像是「建築工人」,它會閱讀藍圖。
* **HTML DOM**:就是工人根據藍圖蓋好的「**房子實體**」。它有完整的結構(樑、柱、牆壁),而 JavaScript 就像是「室內設計師」,可以透過操作這個房子實體,來改變牆壁顏色、移動家具等。
* 每個 HTML 標籤在 JavaScript 引擎中,都會有一個對應的**標籤物件**,稱為 **HTML Element**。
* 瀏覽器將所有這些標籤物件,依照它們在 HTML 中的層級關係串接起來,就形成了樹狀的物件結構,這就是 **HTML DOM**。

---
* **2. DOM 的階層結構**
* 在瀏覽器環境中,所有物件都存在一個階層關係下,其中 `window` 是最頂層的物件。
* **`window` (瀏覽器視窗)**
* **`screen`** (代表使用者的螢幕本身,包含解析度等資訊)
* **`document`** (代表當前載入的網頁文件)
* `<body>` (網頁主體物件)
* `<head>` (網頁頭部物件)
* ... 其他所有 HTML 標籤物件
* **`location`** (代表當前的網址列)
* `alert()` (全域方法)
* `prompt()` (全域方法)

---
* **3. 探索全域物件**
* 因為 `window` 是最頂層的全域物件,所以在呼叫它的屬性或方法時,可以省略 `window.` 這段前綴。
* **`window` 物件**
* **取得屬性 (視窗資訊)**:
```javascript
window.innerWidth; // 取得視窗內部的寬度
window.innerHeight; // 取得視窗內部的高度
```
* **呼叫方法 (互動視窗)**: (已修正您筆記中的 `prompt` 錯字)
```javascript
// alert() 等同於 window.alert()
alert("彈出警告視窗");
// prompt() 等同於 window.prompt()
prompt("輸入資料", "預設值");
```
* **`screen` 物件**
* `screen` 物件是 `window` 的一個屬性,它代表使用者的「實體螢幕」資訊。
* **取得屬性 (螢幕資訊)**:
```javascript
screen.width; // 取得螢幕的總寬度
screen.height; // 取得螢幕的總高度
```
* **`document` 物件**
* `document` 物件是 `window` 的一個屬性,它代表「**整個 HTML 文件**」,是操作網頁元素的入口。
* **取得屬性 (文件資訊)**:
```javascript
document.title; // 取得或設定網頁標題
document.body; // 取得 <body> 標籤物件
```
* **呼叫方法 (選取元素)**:
```javascript
// 這是未來最常用的方法之一,可以透過 CSS 選擇器來選取頁面上的第一個符合條件的元素
document.querySelector("CSS 選擇器");
```
---
* **4. 綜合練習**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript:HTML DOM</title>
</head>
<body>
<script>
// === 認識內建的 window 物件 (HTML DOM 結構的最上層) ===
console.log("window 物件:", window);
// 取得視窗的寬度和高度 (不含工具列)
console.log("視窗內部寬高:", window.innerWidth, window.innerHeight);
// 透過 screen 物件,取得螢幕解析度
console.log("螢幕解析度:", window.screen.width, window.screen.height);
// 取得網址列的內容
console.log("目前網址:", window.location.href);
// 更改網址列的內容 (會直接跳轉網頁,練習時可保持註解)
// window.location.href = "[https://www.google.com](https://www.google.com)";
console.log("--- 分隔線 ---");
// === 認識 document 物件 (代表整個 HTML 文件) ===
console.log("document 物件:", document);
// 取得並更改網頁標題
console.log("原始標題:", document.title);
document.title = "新的網頁標題";
console.log("新標題:", document.title);
// 取得 Body 標籤物件
console.log("body 物件:", document.body);
// 取得並更改網頁主畫面的內容 (Body 標籤的內文)
console.log("原始 body 內容:", document.body.innerHTML);
document.body.innerHTML = "<h1>Hello, HTML DOM</h1><p>內容已被 JavaScript 改變!</p>";
</script>
</body>
</html>
```
:::spoiler 成果展示

:::
---
## 🎬 影片 23: JavaScript HTML DOM 網頁畫面操作演練
### 🎯 本集重點 (Key Takeaways)
* **重點一:DOM 操作三部曲**
* 所有網頁的互動,基本上都遵循一個核心流程:**1. 選取元素** (Select),**2. 操作元素** (Manipulate),**3. 觸發操作** (Trigger)。
* **重點二:`document.querySelector()` 是選取元素的利器**
* 這是最常用、也最有彈性的元素選取方法。它使用 **CSS 選擇器** 作為參數(例如 `#my-id`, `.my-class`),來精準地找到頁面上第一個符合條件的 HTML 元素物件。
* **重點三:三大操作目標**
* 選取到元素後,最常操作的三個目標是:
1. **內容**:透過 `.innerHTML` 來改變標籤內部的 HTML 內容。
2. **樣式(Class)**:透過 `.className` 來置換整個 class,套用預設好的 CSS 樣式。
3. **樣式(Style)**:透過 `.style` 來直接修改特定、單一的行內 CSS 樣式。
* **重點四:事件處理 (Event Handling)**
* 透過 `onclick` 屬性或 `addEventListener` 方法,可以將「使用者的操作(如點擊)」與「特定的 JavaScript 函式」綁定在一起,實現網頁的互動性。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **DOM 操作三部曲**
* 我們可以將所有對網頁畫面的操作,拆解成以下三個步驟:
* **Step 1: 選取元素 (Select)**
* 為了讓 JavaScript 知道要操作「哪一個」HTML 標籤,我們需要先給目標標籤一個獨一無二的識別證,最常用的就是 `id` 屬性。
* **HTML 結構**:
```html
<div id="content">這是一段字</div>
<span id="keyword">這是一段字</span>
```
* **JavaScript 選取**:
```javascript
// 使用 document.querySelector() 並傳入 CSS 選擇器字串
let divElement = document.querySelector('#content');
let spanElement = document.querySelector('#keyword');
```
* **Step 2: 操作元素 (Manipulate)**
* 當我們用變數(如 `divElement`)存好選取到的標籤物件後,就可以像操作一般物件一樣,去改變它的屬性。
* **操作內容 (`.innerHTML`)**:改變標籤包住的內容。
```javascript
divElement.innerHTML = "這是<b>新的</b>字";
```
* **操作 Class (`.className`)**:整個替換掉標籤的 `class` 屬性。
```javascript
divElement.className = "welcome-text";
```
* **操作行內樣式 (`.style`)**:直接修改特定的 CSS 樣式。注意屬性名稱需改為「駝峰式命名」(Camel Case),例如 `font-size` 要寫成 `fontSize`。
```javascript
divElement.style.fontSize = "30px";
divElement.style.color = "blue";
```
* **Step 3: 觸發操作 (Trigger)**
* 這些操作通常不是在網頁載入時就執行,而是在使用者做了「某件事」之後才觸發,例如點擊按鈕。
* **方法一:HTML `onclick` 屬性**
* 直接在 HTML 標籤上寫 `onclick="函式名稱()"`。這種寫法簡單直覺,適合快速的練習。
```html
<div id="content">這是一段字</div>
<button onclick="change();">點我</button>
<script>
function change() {
let divElm = document.querySelector('#content');
divElm.innerHTML = "這是新的字";
divElm.style.color = "blue";
}
</script>
```
> [!TIP]
> **方法二:`addEventListener` (現代推薦的作法)**
>
> 這是更專業、更有彈性的作法,能讓 HTML 和 JavaScript 的職責完全分離。
> ```html
> <button id="my-btn">點我</button>
> ```
> ```javascript
> // 1. 先選取到按鈕
> let btn = document.querySelector('#my-btn');
> // 2. 為按鈕「添加事件聆聽者」
> btn.addEventListener('click', function() {
> // 3. 在這裡寫下點擊後要執行的所有程式
> let divElm = document.querySelector('#content');
> divElm.innerHTML = "這是新的字";
> });
> ```
---
* **綜合練習**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript:HTML DOM 網頁畫面操作</title>
<style>
/* 先定義好一個 CSS class 備用 */
.text {
text-decoration: underline;
font-weight: bold;
}
</style>
</head>
<body>
<h3>HTML DOM 畫面操作</h3>
<div id="content">練習網頁的操作</div>
<button onclick="change()">點我</button>
<script>
function change() {
// 步驟一:選取要操作的標籤物件
let elem = document.querySelector('#content');
// 步驟二:操作標籤物件的各種屬性
elem.innerHTML = "對特定的標籤做操作"; // 改變內容
elem.className = "text"; // 改變 class
elem.style.fontSize = "30px"; // 改變 style
elem.style.color = "red"; // 改變 style
}
</script>
</body>
</html>
```
:::spoiler 成果展示


:::
* **練習 2:點擊文字,切換顯示不同區塊 (Tab 頁籤效果)**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript:HTML DOM 網頁畫面操作</title>
<style>
/* 讓 span 標籤看起來像可以點擊的連結 */
span {
cursor: pointer;
margin: 0 10px;
color: blue;
text-decoration: underline;
}
</style>
</head>
<body>
<div>
<span onclick="changeToAbout();">關於我</span>
<span onclick="changeToExps();">學經歷</span>
</div>
<hr/>
<div id="about">大家好,我是666</div>
<div id="exps" style="display:none">666 666 666 666</div>
<script>
// 點擊「關於我」時要執行的函式
function changeToAbout() {
// 選取要操作的兩個 div
let aboutDiv = document.querySelector("#about");
let expsDiv = document.querySelector("#exps");
// 操作 style,顯示「關於我」,隱藏「學經歷」
aboutDiv.style.display = "block";
expsDiv.style.display = "none";
}
// 點擊「學經歷」時要執行的函式
function changeToExps() {
// 選取要操作的兩個 div
let aboutDiv = document.querySelector("#about");
let expsDiv = document.querySelector("#exps");
// 操作 style,隱藏「關於我」,顯示「學經歷」
aboutDiv.style.display = "none";
expsDiv.style.display = "block";
}
</script>
</body>
</html>
```
:::spoiler 成果展示
 => **點擊關於我**
 => **點擊學經歷**
:::
---
## 🎬 影片 24: JavaScript Event Handling 事件處理
### 🎯 本集重點 (Key Takeaways)
* **重點一:事件 (Event) 是「觸發」**
* 事件是瀏覽器中發生的各種事情,例如使用者「點擊」了按鈕、「滑鼠移入」了圖片,或是整個頁面「載入完成」。
* **重點二:事件處理 (Event Handling) 是「回應」**
* 它是指我們編寫一段特定的 JavaScript 程式碼(稱為事件處理函式),並將它與某個元素的特定事件「綁定」,當事件被觸發時,對應的程式碼就會執行。
* **重點三:`this` 的妙用**
* 在 HTML 的 `on<event>` 屬性中,可以直接傳入 `this` 關鍵字。在這個情境下,`this` 就代表「觸發這個事件的 HTML 元素本身」,讓我們的函式可以更有彈性地操作不同元素。
* **重點四:`addEventListener` 是現代標準**
* 雖然使用 `on<event>` 屬性很直觀,但在現代開發中,使用 `element.addEventListener()` 方法來綁定事件是更推薦、更專業的作法,因為它能更好地做到「關注點分離」。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **1. 什麼是事件處理?**
* **比喻**:事件處理就像是設定一個「自動感應裝置」。
* **事件 (Event)**:有人走過門口(觸發)。
* **事件監聽 (Event Listener)**:安裝在門口的「紅外線感應器」。
* **事件處理函式 (Event Handler)**:感應器觸發後,執行的動作,例如「自動門打開」。
* 透過事件處理,我們的 JavaScript 程式碼就能對使用者的各種操作做出即時的回應。
* **2. 常見的事件種類**
* **滑鼠事件 (Mouse Events)**
* `click`:滑鼠左鍵點擊。
* `mouseover`:滑鼠指標移入元素範圍。
* `mouseout`:滑鼠指標移出元素範圍。
* `mousedown`:滑鼠按鍵被按下(還沒放開)。
* `mouseup`:滑鼠按鍵被放開。
* **鍵盤事件 (Keyboard Events)**
* `keydown`:鍵盤按鍵被按下。
* `keyup`:鍵盤按鍵被放開。
* **表單事件 (Form Events)**
* `submit`:表單被送出時。
* `change`:表單元素的值改變時(例如 `select` 下拉選單)。
* **視窗/文件事件 (Window/Document Events)**
* `load`:整個頁面,包含所有資源(圖片、CSS)都載入完成時。
* `DOMContentLoaded`:HTML 文件本身已經被完全讀取和解析完成時(不等待圖片、CSS)。
---
* **3. 綁定事件的兩種方式**
* **方法一:HTML `on<event>` 屬性 (Inline Method)**
* **說明**:直接在 HTML 標籤上,使用 `on` 加上事件名稱的屬性來綁定要執行的 JavaScript 程式碼或函式。這種寫法很直觀,適合小型練習。
* **基本語法**:
```html
<div onclick="console.log('被點擊了!')">點我</div>
<button onmouseover="myFunction();">滑鼠移過來</button>
```
* **範例一:透過 `this` 傳遞元素**
* 在 `on<event>` 屬性中,`this` 關鍵字就代表該 HTML 元素本身。我們可以把它當作參數傳入函式,讓函式知道是誰觸發了事件。
```html
<div onclick="change(this);">原本的內文</div>
<script>
function change(elem) {
// elem 參數現在就代表被點擊的那個 div 物件
elem.innerHTML = "新的內文";
}
</script>
```
* **範例二:滑鼠按住與放開**
```html
<button onmousedown="down(this);" onmouseup="up(this);">按鈕</button>
<script>
function down(elem) {
elem.style.color = "red";
}
function up(elem) {
elem.style.color = "blue";
}
</script>
```
> [!TIP]
> **方法二:`element.addEventListener()` (Modern Recommended Method)**
>
> **說明**:這是現代 JavaScript 的標準作法。它讓我們可以在 JavaScript 程式中,選取到元素後,再用程式化的方式為它「添加事件監聽器」,完全不需要修改 HTML。
>
> **優點**:
> * **關注點分離**:HTML 專注於結構,JavaScript 專注於行為,程式碼更乾淨、更易維護。
> * **可綁定多個函式**:同一個元素的同一個事件,可以綁定多個不同的處理函式。
> * **功能更強大**:提供更多進階的事件處理選項。
>
> **語法**:
> ```javascript
> let myButton = document.querySelector("#my-btn");
> myButton.addEventListener("click", function() {
> // 當按鈕被點擊時,執行這裡的程式碼
> console.log("按鈕被點擊了!");
> });
> ```
---
* **4. 練習**
- **作法一:** HTML `on<event>` 屬性 (直覺,但混合了 HTML/JS)
* **說明**:這種寫法將事件觸發 (`onmouseover` 等) 直接寫在 `<span>` 標籤內,並呼叫對應的函式。HTML 和 JavaScript 耦合度較高。
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript:事件處理 (方法一)</title>
<style>
.btn {
background-color: #ffcccc; padding: 8px; border-radius: 5px; cursor: pointer;
transition: background-color 0.3s; user-select: none;
}
</style>
</head>
<body>
<h3>事件處理的練習</h3>
<span class="btn" onmouseover="over(this);" onmouseout="out(this);" onmousedown="down(this);" onmouseup="up(this);">點我</span>
<span class="btn" onmouseover="over(this);" onmouseout="out(this);" onmousedown="down(this);" onmouseup="up(this);">第二個按鈕</span>
<script>
function over(elem) { elem.style.backgroundColor = "#ddaaaa"; }
function out(elem) { elem.style.backgroundColor = "#ffcccc"; }
function down(elem) { elem.style.fontWeight = "bold"; }
function up(elem) { elem.style.fontWeight = "normal"; }
</script>
</body>
</html>
```
- **作法二:** 使用 `addEventListener` (推薦,關注點分離)
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript:事件處理 (方法二 - 推薦)</title>
<style>
.btn {
background-color: #ffcccc; padding: 8px; border-radius: 5px; cursor: pointer;
transition: background-color 0.3s; user-select: none;
}
</style>
</head>
<body>
<h3>事件處理的練習</h3>
<span class="btn">點我</span>
<span class="btn">第二個按鈕</span>
<script>
// 1. 先選取到頁面上「所有」class 為 btn 的元素
const allButtons = document.querySelectorAll(".btn");
// 2. 使用 for...of 迴圈 (比 for i=0... 更簡潔),為每一個按鈕都綁定事件
for (const button of allButtons) {
// 綁定 mouseover 事件
button.addEventListener("mouseover", function() {
// 在 addEventListener 的處理函式中,`this` 同樣代表觸發事件的元素本身!
this.style.backgroundColor = "#ddaaaa";
});
// 綁定 mouseout 事件
button.addEventListener("mouseout", function() {
this.style.backgroundColor = "#ffcccc";
});
// 綁定 mousedown 事件
button.addEventListener("mousedown", function() {
this.style.fontWeight = "bold";
});
// 綁定 mouseup 事件
button.addEventListener("mouseup", function() {
this.style.fontWeight = "normal";
});
}
</script>
</body>
</html>
```
:::spoiler 成果展示



:::
---
## 🎬 影片 25: HTTP 通訊協定簡介
### 🎯 本集重點 (Key Takeaways)
* **重點一:HTTP 是網路世界的「通用語言」**
* HTTP (Hypertext Transfer Protocol) 是瀏覽器 (客戶端 Client) 和伺服器 (Server) 之間溝通的基礎通訊協定。所有的網頁資料交換,都依賴這套規則。
* **重點二:溝通模式為「請求與回應 (Request-Response)」**
* 互動永遠由客戶端發起「請求」,伺服器再給予「回應」。就像你點餐(請求),餐廳為你上菜(回應)。
* **重點三:網址 (URL) 是資源的地址**
* 每個網址都由**通訊協定**、**主機名稱**和**路徑**等部分組成,精準地指向網路上某個獨一無二的資源。
* **重點四:兩種主要的資料請求方式**
* **傳統頁面載入**:會造成整個頁面刷新,適合用於不同頁面間的跳轉。
* **AJAX 技術**:只在背景進行資料交換,實現「不刷新頁面,只更新局部內容」的動態效果,是現代網頁應用 (SPA) 的核心。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **1. 網站前後端互動基礎 (Client-Server Model)**
* **比喻**:
* **客戶端 (Client)**:就是你手中的**瀏覽器**,像是一位顧客。
* **伺服器 (Server)**:是存放網站資料的遠端電腦,像是一家餐廳。
* **HTTP**:就是顧客(瀏覽器)和餐廳(伺服器)之間溝通的**語言和點餐流程**。
* 整個互動流程遵循「**請求 (Request) - 回應 (Response)**」模式:瀏覽器發送一個 HTTP 請求給伺服器,伺服器處理完請求後,回傳一個 HTTP 回應(通常是 HTML, CSS, JS 檔案或資料)給瀏覽器。

---
* **2. 網址 (URL) 的組成**
* URL (Uniform Resource Locator) 就是我們俗稱的「網址」,是網路上每個資源(網頁、圖片、影片)的唯一地址。
* `通訊協定://主機名稱/路徑`
* <span style="color:green">**通訊協定 (Protocol)**</span>:`http://` 或 `https://`。它定義了資料傳輸的規則。`https` 是 `http` 的加密版本,更為安全。
* <span style="color:blue">**主機名稱 (Hostname)**</span>:`www.google.com`。它指向存放資源的伺服器電腦的地址。
* <span style="color:red">**路徑 (Path)**</span>:`/search`。它指向伺服器上特定的資源或頁面。
> [!TIP]
> **補充:查詢參數 (Query Parameters)**
>
> 有時候網址後面還會帶有 `?` 開頭的參數,用來傳遞額外的資料給伺服器。例如:`.../search?q=javascript`,這裡的 `q=javascript` 就是一個查詢參數。
---
* **3. 兩種請求方法的區別:傳統頁面載入 vs. AJAX**
* 這是您問題的核心,以下用表格為您清楚區分這兩種方法的不同:
| 比較項目 | 方法一:傳統頁面載入 (Traditional Page Load) | 方法二:AJAX 技術 (Asynchronous JavaScript and XML) |
| :--- | :--- | :--- |
| **觸發方式** | 在網址列輸入網址後按 Enter、點擊 `<a>` 連結、送出傳統表單。 | 由 JavaScript 程式碼在背景觸發 (例如 `fetch()` 函式)。 |
| **頁面反應** | **整個頁面刷新**。瀏覽器會短暫變白,然後重新載入並渲染所有內容。 | **頁面不刷新**。只有需要更新的局部區塊會改變內容。 |
| **使用者體驗** | 體驗較差,有明顯的「跳轉」中斷感。 | 體驗流暢,感覺更像桌面應用程式 (App)。 |
| **資料格式** | 伺服器通常回傳完整的 HTML、CSS、JS 檔案。 | 伺服器通常只回傳**純資料** (最常見的是 JSON 格式)。 |
| **使用情境** | 網站不同大頁面之間的導覽。<br>例如:從首頁點擊到「關於我們」頁面。 | 在同一頁面中進行動態互動。<br>例如:社群網站的「無限滾動」、Google Map 的地圖拖動、留言送出後即時顯示。 |
> [!NOTE]
> AJAX 是一種「概念」,而 `XMLHttpRequest` (XHR) 或現代更推薦的 `fetch()` API 則是實現這個概念的具體 JavaScript 技術。
* **`fetch()` 簡單範例**:
這段程式碼展示了如何用 JavaScript 在背景發送請求,取得資料,並且不刷新頁面。
```javascript
// 使用 fetch 向一個公開的 API 發送請求
fetch('[https://jsonplaceholder.typicode.com/todos/1](https://jsonplaceholder.typicode.com/todos/1)')
.then(response => response.json()) // 將回應轉換為 JSON 資料格式
.then(data => {
// 成功取得資料後,在主控台印出
console.log(data);
// 在這裡,我們就可以用 JS 把 data 的內容更新到畫面上
});
```
---
## 🎬 影片 26: AJAX 網路連線實務
### 🎯 本集重點 (Key Takeaways)
* **重點一:`fetch()` 是現代網路請求的標準**
* `fetch()` 是 JavaScript 內建的函式,用來取代舊的 XHR 技術,是目前執行 AJAX 網路連線最主流、最推薦的方法。
* **重點二:`fetch()` 的運作是「非同步 (Asynchronous)」的**
* 當 `fetch()` 發送請求後,程式不會停下來等待,而是會繼續往下執行。當伺服器回應時,才會透過 `.then()` 中的回呼函式來處理結果。
* **重點三:`.then()` 鏈式呼叫處理流程**
* `fetch()` 使用 Promise Chaining (`.then().then()`) 來處理非同步流程。第一個 `.then()` 負責接收初步的 `Response` 物件並解析它;第二個 `.then()` 才能真正取得處理完畢的資料。
* **重點四:回應格式的處理是關鍵**
* `Response` 物件必須透過對應的方法來解析。如果預期收到純文字,就用 **`.text()`**;如果預期收到 JSON 資料,就用 **`.json()`**,後者會自動將 JSON 字串轉換成 JavaScript 物件或陣列。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **1. AJAX 技術的演進**
* **初期 (AJAX)**:全名為 Asynchronous JavaScript And XML,是一個概念,早期資料格式多用 XML。
* **中期 (XHR)**:透過 `XMLHttpRequest` 物件來實作 AJAX,語法較為繁瑣。
* **現代 (`fetch`)**:近年來,`fetch` 函式因其更簡潔的語法和基於 Promise 的強大功能,已成為執行網路請求的首選。
---
* **2. `fetch()` 的基本流程:非同步與 Promise**
* **比喻**:`fetch()` 就像是「**線上點餐**」。
1. `fetch(網址)`:你送出訂單,立刻拿到一張「**取餐憑證 (Promise)**」。你不用在櫃檯傻等,可以先去做自己的事(程式繼續往下執行)。
2. `第一個 .then()`:餐廳(伺服器)準備好餐點了,通知你來取餐。你拿到的是一個完整的「**便當盒 (Response 物件)**」,裡面有餐點、餐具、發票等資訊,但你還沒打開它。
3. `return response.json()`:你決定要「**打開便當盒,準備吃飯**」(解析 Response)。`.json()` 就是打開 JSON 格式的便當盒,`.text()` 就是打開純文字格式的。
4. `第二個 .then()`:你終於看到了便當裡的「**主菜 (真正的資料 data)**」,現在可以開始享用了(將資料渲染到畫面上)。
* **基本語法**:
```javascript
fetch(網址).then(function(回應物件) {
// 在這裡解析回應物件
}).then(function(真正的資料) {
// 在這裡使用真正的資料
});
```
---
* **3. 處理不同格式的回應:方法一 vs. 方法二**
* 瀏覽器送出請求後,我們無法百分之百確定伺服器會回傳什麼格式的資料。因此,`fetch` 的第一個 `.then()` 拿到 `Response` 物件後,需要我們手動指定用什麼「方法」來解析它。
* **方法一:取得純文字 (`.text()`)**
* **使用時機**:當你預期後端回傳的是一段純文字、HTML 碼、或任何非 JSON 格式的字串時使用。
* **回傳結果**:第二個 `.then()` 中的 `data` 會是一個 **`String` (字串)** 型態。
```javascript
fetch(網址).then(function(response) {
return response.text(); // 將回應主體解析為純文字
}).then(function(data) {
console.log(data); // data 是一個字串
});
```
* **方法二:取得 JSON 格式 (`.json()`)**
* **使用時機**:當你預期後端回傳的是 JSON 格式的資料時使用,這是現代網頁開發中最常見的資料交換格式。
* **回傳結果**:`.json()` 方法會自動將 JSON 字串**解析**成 JavaScript 的 **`Object` (物件)** 或 **`Array` (陣列)**。第二個 `.then()` 中的 `data` 就會是 JS 物件/陣列,可以直接操作。
```javascript
fetch(網址).then(function(response) {
return response.json(); // 將回應主體解析為 JS 物件/陣列
}).then(function(data) {
console.log(data); // data 是一個 JS 物件或陣列
});
```
> [!TIP]
> **補充:錯誤處理 (`.catch()`)**
>
> 如果網路連線失敗(例如斷線、網址錯誤),程式會中斷。我們可以加上 `.catch()` 來捕捉錯誤,並執行對應的處理,讓程式更穩定。
> ```javascript
> fetch(網址).then(...).then(...).catch(function(error) {
> console.log("連線失敗:", error);
> });
> ```
---
* **4. 綜合練習**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript:AJAX 網路連線實務</title>
</head>
<body>
<h3>AJAX / XHR 網路連線實務</h3>
<button onclick="getData();">連線取得資料</button>
<div id="result"></div>
<script>
/* 測試用的連線網址(會回傳一個包含多個產品物件的 JSON 陣列):
https://cwpeng.github.io/live-records-samples/data/products.json
*/
function getData() {
// 1. 利用 fetch 發送網路請求
fetch("https://cwpeng.github.io/live-records-samples/data/products.json")
.then(function(response) {
// 2. 收到回應後,因為我們知道資料是 JSON 格式,所以使用 .json() 來解析它
return response.json();
})
.then(function(data) {
// 3. 成功解析後,data 就是我們能直接使用的 JavaScript 陣列了
console.log("成功取得的資料:", data);
// 4. 將資料呈現在畫面上
// - 先選取到要放置結果的 div
// - 修正變數名稱錯誤 (querySelector 的參數是 "#result")
let resultDiv = document.querySelector("#result");
// - 每次點擊都先清空之前的結果
// - 修正變數名稱錯誤 (resule -> resultDiv)
resultDiv.innerHTML = "";
// - 用 for 迴圈遍歷陣列中的每一個產品物件
for (let i = 0; i < data.length; i++) {
let product = data[i]; // 取得單一產品物件
// - 將產品資訊組合成 HTML 字串,並累加到 resultDiv 中
resultDiv.innerHTML += "<div>" + product.name + " - 價格:" + product.price + " - 描述:" + product.description + "</div>";
}
})
.catch(function(error){
// (補充) 如果連線失敗,就在主控台印出錯誤訊息
console.log("錯誤:", error);
});
}
</script>
</body>
</html>
```
::: spoiler 成果展示


:::
---
## 🎬 影片 27: JavaScript 箭頭函式 Arrow Function
### 🎯 本集重點 (Key Takeaways)
* **重點一:更簡潔的函式語法**
* 箭頭函式 (Arrow Function) 是 ES6 新增的一種函式寫法,它省略了 `function` 關鍵字,並使用一個 `=>` (fat arrow),讓函式的語法更簡潔、更易讀。
* **重點二:可根據情境簡化**
* 在特定條件下,箭頭函式可以進一步簡化,例如:當函式只有一個參數時,可省略 `()`;當函式主體只有一行 `return` 敘述時,可省略 `{}` 和 `return`。
* **重點三:`this` 的行為不同 (最關鍵差異)**
* 傳統函式的 `this` 指向是動態的,取決於「如何被呼叫」;而箭頭函式沒有自己的 `this`,它會捕捉其被建立時所在的「外部作用域」的 `this` 值。這個特性解決了許多傳統 `this` 指向混亂的問題。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **1. 從傳統函式到箭頭函式**
* 箭頭函式是「函式表達式」的一種語法糖 (Syntactic Sugar),讓寫法更精簡。
* **傳統寫法一 (函式宣告)**
```javascript
function add(n1, n2) {
let result = n1 + n2;
return result;
}
```
* **傳統寫法二 (函式表達式)**
```javascript
let add = function(n1, n2) {
let result = n1 + n2;
return result;
};
```
* **箭頭函式基本寫法**
* 省略 `function` 關鍵字,在參數列表後方加上 `=>`。
```javascript
let add = (n1, n2) => {
let result = n1 + n2;
return result;
};
```
---
* **2. 箭頭函式的簡化規則**
* **規則一:如果函式主體只有「一行 `return` 敘述」**
* 可以同時省略大括號 `{}` 和 `return` 關鍵字。
* **範例**:
```javascript
// 可簡化的版本
let add = (n1, n2) => {
return n1 + n2;
};
// 簡化後的版本 (Implicit Return)
let addSimplified = (n1, n2) => (n1 + n2);
```
* **規則二:如果參數「只有一個」**
* 可以省略參數外面的小括號 `()`。
* **範例**:
```javascript
// 原本的寫法
let square = (x) => { return x * x; };
// 簡化後的版本
let squareSimplified = x => x * x;
```
* **規則三:如果「沒有參數」**
* 必須保留一對空的 `()`。
* **範例**:
```javascript
let sayHello = () => console.log("Hello");
```
---
* **3. 關鍵差異:`this` 的綁定**
* **傳統函式**:`this` 的值取決於**函式如何被呼叫**,指向是動態的,容易造成混亂。
* **箭頭函式**:**沒有自己的 `this`**。它會像普通變數一樣,捕捉並繼承其**外層作用域**的 `this` 值。這個 `this` 在函式定義時就已確定,不會再改變。
> **比喻**:傳統函式像個「外包人員」,`this`(老闆)是誰,取決於是哪個物件「雇用」了它。箭頭函式則像個「內部員工」,它的 `this`(老闆)永遠是它被建立時所在的那個部門(作用域),非常忠誠。
---
* **4. 綜合練習**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>箭頭函式 Arrow Function</title>
</head>
<body>
<script>
// === 傳統函式:計算 1+2+...+10 ===
function calculate() {
let result = 0;
let n = 1;
while (n <= 10) {
result += n;
n++;
}
console.log("傳統函式(無參數)結果:", result);
}
calculate();
// === 傳統函式:計算 1+2+...+max ===
function calculate2(max) {
let result = 0;
let n = 1;
while (n <= max) {
result += n;
n++;
}
return result;
}
let ans1 = calculate2(10);
let ans2 = calculate2(20);
console.log("傳統函式(有參數)結果:", ans1, ans2);
console.log("--- 使用箭頭函式 ---");
// === 箭頭函式:建立 calculate3 ===
let calculate3 = (max) => {
let result = 0;
let n = 1;
while (n <= max) {
result += n;
n++;
}
return result;
}
console.log("箭頭函式(基本)結果:", calculate3(10));
// === 箭頭函式:簡化語法 ===
// 這是可以簡化的版本
/*
let multiply = (n1, n2) => {
return n1 * n2;
};
*/
// 簡化後:因為只有一行 return,所以省略 {} 和 return
let multiply = (n1, n2) => (n1 * n2);
let ans = multiply(3, 4);
console.log("箭頭函式(簡化)結果:", ans);
</script>
</body>
</html>
```
:::spoiler 成果展示

:::
---
## 🎬 影片 28: JavaScript 解構賦值 Destructuring Assignment
### 🎯 本集重點 (Key Takeaways)
* **重點一:更簡潔的取值語法**
* 解構賦值 (Destructuring Assignment) 是一種 ES6 的語法糖,讓我們可以更快速、更直觀地從陣列或物件中「提取」資料,並直接賦值給變數。
* **重點二:語法鏡像結構**
* 解構的語法與資料本身的結構相對應:用中括號 `[]` 來解構陣列,用大括號 `{}` 來解構物件。
* **重點三:陣列靠「順序」,物件靠「名稱」**
* **陣列解構**是按照**順序**(索引)來賦值的。
* **物件解構**是按照**屬性名稱 (key)** 來配對賦值的,順序不重要。
* **重點四:實用技巧**
* 解構賦值讓「變數交換」、設定「預設值」以及處理「函式參數」等常見情境變得極其優雅。
### 📝 詳細筆記與心得 (Notes & Reflections)
* **1. 什麼是解構賦值?**
* 它的核心思想是:**將一個複雜的資料結構(陣列或物件)拆解開,並將其內部的資料,一次性地賦值給多個獨立的變數。**
* **比喻**:就像你拿到一個禮物籃(陣列/物件),解構賦值讓你可以一個動作就把裡面的「蘋果、香蕉、卡片」(資料)直接放到對應的盤子(變數)裡,而不需要一個一個慢慢拿。
---
* **2. 陣列的解構賦值 (Array Destructuring)**
* 陣列解構是基於**順序**進行的。
* **基本用法**
* **傳統作法**:
```javascript
let arr = [3, 4, 5];
let d1 = arr[0];
let d2 = arr[1];
let d3 = arr[2];
```
* **解構賦值**:
```javascript
let arr = [3, 4, 5];
let [d1, d2, d3] = arr; // 一行搞定
console.log(d1, d2, d3); // 3 4 5
```
* **進階技巧**
* **宣告與賦值分開**:
```javascript
let d1, d2, d3;
[d1, d2, d3] = [3, 4, 5];
```
* **設定預設值**:當陣列中的元素數量不足時,可以提供預設值。
```javascript
let [d1, d2, d3 = 10] = [3, 4];
console.log(d1, d2, d3); // 3 4 10 (d3 使用了預設值)
```
* **(補充) 剩餘模式 (Rest Pattern)**:取得剩餘的所有元素,打包成一個新陣列。
```javascript
let [d1, ...rest] = [3, 4, 5, 6];
console.log(d1); // 3
console.log(rest); // [4, 5, 6]
```
---
* **3. 物件的解構賦值 (Object Destructuring)**
* 物件解構是基於**屬性名稱 (key)** 進行的,與順序無關。
* **基本用法**
* **傳統作法**:
```javascript
let obj = { x: 3, y: 4 };
let x = obj.x;
let y = obj.y;
```
* **解構賦值**:
```javascript
let obj = { x: 3, y: 4 };
// 變數名稱必須和物件的 key 相同
let { x, y } = obj;
console.log(x, y); // 3 4
```
* **進階技巧**
* **宣告與賦值分開**:需要用 `()` 包裹,避免 JavaScript 將 `{}` 誤認為是程式碼區塊。
```javascript
let x, y;
let obj = { x: 3, y: 4 };
({ x, y } = obj); // 必須用小括號包起來
```
* **設定預設值**:
```javascript
let { x, y = 5 } = { x: 3 };
console.log(x, y); // 3 5 (y 使用了預設值)
```
* **重新命名變數**:如果不想使用和 key 相同的變數名稱,可以指定一個新的名字。
```javascript
let obj = { x: 3, y: 4 };
let { x: newX, y: newY } = obj;
console.log(newX, newY); // 3 4
```
---
* **4. 實用的解構賦值技巧**
* **技巧一:交換變數 (Swapping Variables)**
* 傳統需要一個暫存變數,解構賦值只需一行。
```javascript
let n1 = 3;
let n2 = 4;
[n1, n2] = [n2, n1]; // 優雅地交換
console.log(n1, n2); // 4 3
```
* **技巧二:函式參數解構 (Destructuring Function Parameters)**
* 讓函式接收物件參數時,可以直接在參數列表進行解構,使程式碼更簡潔、易讀。
* **傳統作法**:
```javascript
function add(args) {
console.log(args.n1 + args.n2);
}
add({ n1: 3, n2: 4 });
```
* **解構賦值**:
```javascript
function add({ n1, n2 }) { // 直接在參數處解構
console.log(n1 + n2);
}
add({ n1: 3, n2: 4 });
```
---
* **5. 綜合練習**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>解構賦值 Destructuring Assignment</title>
</head>
<body>
<script>
// === 陣列的解構賦值 ===
console.log("--- 陣列解構 ---");
let arr = [3, 4, 5];
// 使用解構賦值,並為 d4 設定預設值
let d1, d2, d3, d4;
[d1, d2, d3, d4 = 10] = arr;
console.log(d1, d2, d3, d4); // 3 4 5 10
// === 技巧一:變數資料交換 ===
console.log("--- 變數交換 ---");
let n1 = 3;
let n2 = 4;
console.log("交換前:", n1, n2); // 3 4
[n1, n2] = [n2, n1];
console.log("交換後:", n1, n2); // 4 3
// === 物件的解構賦值 ===
console.log("--- 物件解構 ---");
let obj = { x: 3, y: 4, z: 5 };
// 使用解構賦值,同時重新命名變數,並設定預設值
let newX, newY, newZ;
({ x: newX, y: newY, z: newZ = 10 } = obj);
console.log(newX, newY, newZ); // 3 4 5
// === 技巧二:函式的物件參數解構 ===
console.log("--- 函式參數解構 ---");
// 直接在函式的參數列表中解構傳入的物件
function add({ n1, n2 }) {
console.log(n1 + n2);
}
// 呼叫函式時,傳入一個物件
add({ n1: 3, n2: 4 }); // 7
</script>
</body>
</html>
```
:::spoiler 成果展示

:::