線上課程觀課進度管理小工具開發日誌
Day 6:JavaScript 程式碼執行排序:遞迴函數、Call Stack、Task Queue
Call Stack;遞迴函數與 call stack 的關係;Task Queue(非同步的概念再釐清)
Day 9:「修煉圈圈 Practice Rings」Web app 開發日誌 - 2025 六角 AI Vibe Coding 體驗營期末大魔王作業
這份筆記旨在記錄 2025 六角 Vibe Coding 體驗營每日作業 Day 21 的期末專案成果,從一個簡單的個人痛點出發,透過與 AI(在我這個情境中是 Perplexity)協作,在一天內完成了一個包含前後端的全端網頁小工具:「修煉圈圈 Practice Rings」。
記錄雞蛋糕的每一步前端煉成過程,從小白到(也許是)前端工程師的學習與分享。
線上課程觀課進度管理小工具開發日誌
Day 6:JavaScript 程式碼執行排序:遞迴函數、Call Stack、Task Queue
Call Stack;遞迴函數與 call stack 的關係;Task Queue(非同步的概念再釐清)
Day 9:「修煉圈圈 Practice Rings」Web app 開發日誌 - 2025 六角 AI Vibe Coding 體驗營期末大魔王作業
這份筆記旨在記錄 2025 六角 Vibe Coding 體驗營每日作業 Day 21 的期末專案成果,從一個簡單的個人痛點出發,透過與 AI(在我這個情境中是 Perplexity)協作,在一天內完成了一個包含前後端的全端網頁小工具:「修煉圈圈 Practice Rings」。
記錄雞蛋糕的每一步前端煉成過程,從小白到(也許是)前端工程師的學習與分享。

Subscribe to 雞蛋糕的前端修煉屋

Subscribe to 雞蛋糕的前端修煉屋
<100 subscribers
<100 subscribers
2/16 - 2/20 農曆年假休息,少量看台大 Vibe coding Web app 開發開放式課程
📅 2026-02-23 | 🏷 JavaScript · OOP · HTML · CSS
JavaScript 採用原型導向(prototype-based),而非 Java、C++ 那種類別導向(class-based)。ECMAScript 規格書明確指出:
"ECMAScript objects are not fundamentally class-based such as those in C++, Smalltalk, or Java." tc39
兩者的根本差異在於繼承機制:傳統 OOP 繼承的是「結構 + 行為」,JavaScript 的原型鏈則可以連「狀態」一起繼承。 developer.mozilla
機制 | 傳統 OOP(Java / C++) | JavaScript |
|---|---|---|
狀態由誰攜帶 | 實例(instance) | 物件本身 |
方法由誰攜帶 | 類別(class) | 物件本身 |
繼承的是什麼 | 結構 + 行為 | 結構 + 行為 + 狀態 |
繼承機制 | 類別繼承 |
|
術語 | 白話解釋 | 汽車比喻 |
|---|---|---|
類別(class) | 設計藍圖 | 工廠設計圖紙 |
實例(instance) | 照藍圖造出的實體 | 真正生產的一台車 |
狀態(state) | 物件目前持有的資料 | 油量、顏色、時速 |
行為(behavior) | 物件能做的事 | 加速、剎車、開燈 |
方法(method) | 行為的程式碼實作 |
|
結構(structure) | 物件有哪些屬性名稱 | 規格表欄位 |
繼承(inheritance) | 子類別自動獲得父類別的一切 | 電動車沿用汽車設計 |
// 直接 return 物件,呼叫時不需要 new
const createCar = (brand, color) => ({
brand,
color,
drive() {
return `${brand} 行駛中`; // 透過閉包存取參數,不需要 this
},
});
const bmw = createCar('BMW', 'red');
不需要 new,呼叫方式與普通函式相同
方法各自獨立,每個實例自己持有一份(記憶體消耗較高)
無 prototype 鏈,instanceof 無效
閉包天然封裝狀態,不依賴 this,不會有 this 跑掉的問題 developer.mozilla
function Car(brand, color) {
this.brand = brand; // 每個實例自己的狀態
this.color = color;
}
// 方法手動掛到 prototype,所有實例共用同一份
Car.prototype.drive = function () {
return `${this.brand} 行駛中`;
};
const bmw = new Car('BMW', 'red'); // 忘記 new → this 指向全域物件
忘記 new 是最常見的 bug,this 會默默指向 window
方法共用 prototype,省記憶體,適合大量實例 developer.mozilla
支援 instanceof 型別判斷
實務建議:新專案改用 class,此寫法僅需看懂 legacy code
class Car {
#mileage = 0; // ES2020 真正的私有欄位,prototype 模式無法複製
constructor(brand, color) {
this.brand = brand;
this.color = color;
}
// 自動掛到 Car.prototype,不需要手動設定
drive() {
return `${this.brand} 行駛中`;
}
addMileage(km) {
this.#mileage += km;
}
}
const bmw = new Car('BMW', 'red');
底層與建構子函式完全相同,方法自動掛到 prototype developer.mozilla
支援 extends 繼承,語法簡潔直觀
自動開啟嚴格模式(strict mode),這是與建構子函式的重要差異
私有欄位 # 是 ES2020 真正新增的語言能力,並非語法糖
「class 是語法糖」大部分正確,但 ES2020 私有欄位
#是例外。
this 的核心任務是:讓共用的方法知道「現在是哪個實例在呼叫我」。
Car.prototype
┌──────────────┐
│ drive() │ ← 全部實例共用同一個 function
└──────┬───────┘
│ 被誰呼叫?
┌──────┴───────┐
▼ ▼
bmw.drive() benz.drive()
this = bmw this = benz ← this 動態指向呼叫者
this的指向取決於「怎麼呼叫」,不是「在哪裡定義」。
const bmw = new Car('BMW');
bmw.drive(); // ✅ this = bmw
const fn = bmw.drive; // 把方法「拆出來」
fn(); // ❌ class 嚴格模式下 this = undefined → TypeError
呼叫方式 | 執行環境 |
|
|---|---|---|
| 任何模式 |
|
| 非嚴格模式 |
|
| 嚴格模式 / class |
|
設計理念:寧可立刻看到 TypeError,也不要讓 this 默默污染 window。 developer.mozilla
TypeScript 的核心價值:把執行期才會爆的錯誤,提前到編譯期攔截。
class Car {
brand = 'BMW';
drive(this: Car) { // 標註 this 必須是 Car 實例
return `${this.brand} 行駛中`;
}
}
const fn = new Car().drive;
fn(); // ❌ 編譯期直接報錯,不用等執行
// 傳錯型別
function setSpeed(speed: number) {}
setSpeed('fast'); // ❌ 編譯期報錯
// 存取不存在的屬性
const car = { brand: 'BMW' };
car.color; // ❌ Property 'color' does not exist
// class 屬性沒初始化
class Car {
brand: string; // ❌ 沒有 initializer 也沒在 constructor 賦值
}
建議開啟 tsconfig.json:
{
"compilerOptions": {
"strict": true // 包含 noImplicitThis、strictNullChecks 等
}
}
防止意外覆寫:const 確保函式參考不被覆蓋,function 宣告式則沒有這個保護。 stackoverflow
const createCar = (brand) => ({ brand });
createCar = () => {}; // ❌ TypeError 立刻報錯
沒有自己的 this:箭頭函式從語法層面斷掉 this,與工廠函式「不依賴 this」的設計一致。
⚠️ TDZ 限制:const 沒有 Hoisting,宣告前呼叫會拋出 ReferenceError。解法:把工廠函式的定義放在呼叫之前。
|
|
| |
|---|---|---|---|
防止意外覆寫 | |||
定義前可呼叫(Hoisting) | TDZ 報錯 | TDZ 報錯 | |
有自己的 | (工廠函式不需要) | ||
Airbnb 風格推薦 | 可接受 | 可接受 |
需要繼承(extends)嗎?
├── 是 → class
└── 否 ↓
會產生大量實例(百個以上)嗎?
├── 是 → class(方法共用 prototype 省記憶體)
└── 否 ↓
需要封裝私有狀態、或方便測試注入?
├── 是 → 工廠函式
└── 否 → 都可以,選團隊熟悉的
// 依賴注入:db 可從外部傳入假資料,方便 TDD
const createUserService = (db) => ({
async getUser(id) {
return db.find(id);
},
});
const fakeDb = { find: () => ({ name: 'Alice' }) };
const service = createUserService(fakeDb);
適合:需要閉包封裝私有狀態、重視測試性、Vue3 Composition API / React Hooks 等函式組合風格。 stackoverflow
工廠函式 | 建構子函式 | Class | |
|---|---|---|---|
需要 | |||
方法共用 prototype | 各自一份 | ||
支援 | |||
支援 | 手動且複雜 | ||
私有欄位 |
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>頁面標題</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- 畫面內容放這裡 -->
</body>
</html>
標籤 / 屬性 | 作用 | 重要備注 |
|---|---|---|
| 宣告 HTML5 標準模式解析 stackoverflow | 缺少會進入「怪異模式」,CSS 排版出錯 |
| 文件根元素,宣告語言 developer.mozilla | 影響螢幕閱讀器、SEO、翻譯提示 |
| 宣告 UTF-8 編碼,支援中文與 emoji stackoverflow | 必須放在 |
| 行動裝置適配指令 developer.mozilla | 缺少此行,RWD 全部失效 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
width=device-width — 版面寬度 = 裝置螢幕實際寬度
initial-scale=1.0 — 初始縮放比例 1:1,不放大也不縮小
三種核心行為決定元素如何在頁面流中排列: developer.mozilla
類型 | 前後換行 | 可設寬高 | 代表元素 |
|---|---|---|---|
| 強制換行,撐滿父容器 |
| |
| 不換行,跟著文字流 |
| |
| 不換行 |
|
元素 | 預設 display | 說明 |
|---|---|---|
|
| 類似 block,但有 |
|
| 外部像 block,內部有表格排版邏輯 |
|
| 不渲染在畫面上 |
對 inline 元素設 width/height 無效 — 需先改成 display: inline-block 或 block
<img> 底部有神秘空白 — 因為是 inline-block,會基線對齊;解法:display: block 或 vertical-align: bottom
<li> 改成 block 會讓項目符號消失 — list-item 多了 ::marker 虛擬元素
lang 屬性記得改 — 中文網站用 lang="zh-TW",影響無障礙、SEO 與翻譯功能 developer.mozilla
2/16 - 2/20 農曆年假休息,少量看台大 Vibe coding Web app 開發開放式課程
📅 2026-02-23 | 🏷 JavaScript · OOP · HTML · CSS
JavaScript 採用原型導向(prototype-based),而非 Java、C++ 那種類別導向(class-based)。ECMAScript 規格書明確指出:
"ECMAScript objects are not fundamentally class-based such as those in C++, Smalltalk, or Java." tc39
兩者的根本差異在於繼承機制:傳統 OOP 繼承的是「結構 + 行為」,JavaScript 的原型鏈則可以連「狀態」一起繼承。 developer.mozilla
機制 | 傳統 OOP(Java / C++) | JavaScript |
|---|---|---|
狀態由誰攜帶 | 實例(instance) | 物件本身 |
方法由誰攜帶 | 類別(class) | 物件本身 |
繼承的是什麼 | 結構 + 行為 | 結構 + 行為 + 狀態 |
繼承機制 | 類別繼承 |
|
術語 | 白話解釋 | 汽車比喻 |
|---|---|---|
類別(class) | 設計藍圖 | 工廠設計圖紙 |
實例(instance) | 照藍圖造出的實體 | 真正生產的一台車 |
狀態(state) | 物件目前持有的資料 | 油量、顏色、時速 |
行為(behavior) | 物件能做的事 | 加速、剎車、開燈 |
方法(method) | 行為的程式碼實作 |
|
結構(structure) | 物件有哪些屬性名稱 | 規格表欄位 |
繼承(inheritance) | 子類別自動獲得父類別的一切 | 電動車沿用汽車設計 |
// 直接 return 物件,呼叫時不需要 new
const createCar = (brand, color) => ({
brand,
color,
drive() {
return `${brand} 行駛中`; // 透過閉包存取參數,不需要 this
},
});
const bmw = createCar('BMW', 'red');
不需要 new,呼叫方式與普通函式相同
方法各自獨立,每個實例自己持有一份(記憶體消耗較高)
無 prototype 鏈,instanceof 無效
閉包天然封裝狀態,不依賴 this,不會有 this 跑掉的問題 developer.mozilla
function Car(brand, color) {
this.brand = brand; // 每個實例自己的狀態
this.color = color;
}
// 方法手動掛到 prototype,所有實例共用同一份
Car.prototype.drive = function () {
return `${this.brand} 行駛中`;
};
const bmw = new Car('BMW', 'red'); // 忘記 new → this 指向全域物件
忘記 new 是最常見的 bug,this 會默默指向 window
方法共用 prototype,省記憶體,適合大量實例 developer.mozilla
支援 instanceof 型別判斷
實務建議:新專案改用 class,此寫法僅需看懂 legacy code
class Car {
#mileage = 0; // ES2020 真正的私有欄位,prototype 模式無法複製
constructor(brand, color) {
this.brand = brand;
this.color = color;
}
// 自動掛到 Car.prototype,不需要手動設定
drive() {
return `${this.brand} 行駛中`;
}
addMileage(km) {
this.#mileage += km;
}
}
const bmw = new Car('BMW', 'red');
底層與建構子函式完全相同,方法自動掛到 prototype developer.mozilla
支援 extends 繼承,語法簡潔直觀
自動開啟嚴格模式(strict mode),這是與建構子函式的重要差異
私有欄位 # 是 ES2020 真正新增的語言能力,並非語法糖
「class 是語法糖」大部分正確,但 ES2020 私有欄位
#是例外。
this 的核心任務是:讓共用的方法知道「現在是哪個實例在呼叫我」。
Car.prototype
┌──────────────┐
│ drive() │ ← 全部實例共用同一個 function
└──────┬───────┘
│ 被誰呼叫?
┌──────┴───────┐
▼ ▼
bmw.drive() benz.drive()
this = bmw this = benz ← this 動態指向呼叫者
this的指向取決於「怎麼呼叫」,不是「在哪裡定義」。
const bmw = new Car('BMW');
bmw.drive(); // ✅ this = bmw
const fn = bmw.drive; // 把方法「拆出來」
fn(); // ❌ class 嚴格模式下 this = undefined → TypeError
呼叫方式 | 執行環境 |
|
|---|---|---|
| 任何模式 |
|
| 非嚴格模式 |
|
| 嚴格模式 / class |
|
設計理念:寧可立刻看到 TypeError,也不要讓 this 默默污染 window。 developer.mozilla
TypeScript 的核心價值:把執行期才會爆的錯誤,提前到編譯期攔截。
class Car {
brand = 'BMW';
drive(this: Car) { // 標註 this 必須是 Car 實例
return `${this.brand} 行駛中`;
}
}
const fn = new Car().drive;
fn(); // ❌ 編譯期直接報錯,不用等執行
// 傳錯型別
function setSpeed(speed: number) {}
setSpeed('fast'); // ❌ 編譯期報錯
// 存取不存在的屬性
const car = { brand: 'BMW' };
car.color; // ❌ Property 'color' does not exist
// class 屬性沒初始化
class Car {
brand: string; // ❌ 沒有 initializer 也沒在 constructor 賦值
}
建議開啟 tsconfig.json:
{
"compilerOptions": {
"strict": true // 包含 noImplicitThis、strictNullChecks 等
}
}
防止意外覆寫:const 確保函式參考不被覆蓋,function 宣告式則沒有這個保護。 stackoverflow
const createCar = (brand) => ({ brand });
createCar = () => {}; // ❌ TypeError 立刻報錯
沒有自己的 this:箭頭函式從語法層面斷掉 this,與工廠函式「不依賴 this」的設計一致。
⚠️ TDZ 限制:const 沒有 Hoisting,宣告前呼叫會拋出 ReferenceError。解法:把工廠函式的定義放在呼叫之前。
|
|
| |
|---|---|---|---|
防止意外覆寫 | |||
定義前可呼叫(Hoisting) | TDZ 報錯 | TDZ 報錯 | |
有自己的 | (工廠函式不需要) | ||
Airbnb 風格推薦 | 可接受 | 可接受 |
需要繼承(extends)嗎?
├── 是 → class
└── 否 ↓
會產生大量實例(百個以上)嗎?
├── 是 → class(方法共用 prototype 省記憶體)
└── 否 ↓
需要封裝私有狀態、或方便測試注入?
├── 是 → 工廠函式
└── 否 → 都可以,選團隊熟悉的
// 依賴注入:db 可從外部傳入假資料,方便 TDD
const createUserService = (db) => ({
async getUser(id) {
return db.find(id);
},
});
const fakeDb = { find: () => ({ name: 'Alice' }) };
const service = createUserService(fakeDb);
適合:需要閉包封裝私有狀態、重視測試性、Vue3 Composition API / React Hooks 等函式組合風格。 stackoverflow
工廠函式 | 建構子函式 | Class | |
|---|---|---|---|
需要 | |||
方法共用 prototype | 各自一份 | ||
支援 | |||
支援 | 手動且複雜 | ||
私有欄位 |
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>頁面標題</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- 畫面內容放這裡 -->
</body>
</html>
標籤 / 屬性 | 作用 | 重要備注 |
|---|---|---|
| 宣告 HTML5 標準模式解析 stackoverflow | 缺少會進入「怪異模式」,CSS 排版出錯 |
| 文件根元素,宣告語言 developer.mozilla | 影響螢幕閱讀器、SEO、翻譯提示 |
| 宣告 UTF-8 編碼,支援中文與 emoji stackoverflow | 必須放在 |
| 行動裝置適配指令 developer.mozilla | 缺少此行,RWD 全部失效 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
width=device-width — 版面寬度 = 裝置螢幕實際寬度
initial-scale=1.0 — 初始縮放比例 1:1,不放大也不縮小
三種核心行為決定元素如何在頁面流中排列: developer.mozilla
類型 | 前後換行 | 可設寬高 | 代表元素 |
|---|---|---|---|
| 強制換行,撐滿父容器 |
| |
| 不換行,跟著文字流 |
| |
| 不換行 |
|
元素 | 預設 display | 說明 |
|---|---|---|
|
| 類似 block,但有 |
|
| 外部像 block,內部有表格排版邏輯 |
|
| 不渲染在畫面上 |
對 inline 元素設 width/height 無效 — 需先改成 display: inline-block 或 block
<img> 底部有神秘空白 — 因為是 inline-block,會基線對齊;解法:display: block 或 vertical-align: bottom
<li> 改成 block 會讓項目符號消失 — list-item 多了 ::marker 虛擬元素
lang 屬性記得改 — 中文網站用 lang="zh-TW",影響無障礙、SEO 與翻譯功能 developer.mozilla
依賴注入友善 | 較麻煩 |
實務推薦 | 現代主流 | 僅維護舊碼 | 繼承 / 大量實例 |
| 瀏覽器分頁名稱 + 搜尋結果標題 | 對 SEO 影響大 |
| 載入外部 CSS | 瀏覽器讀到此行會暫停渲染,下載完才繼續 |
依賴注入友善 | 較麻煩 |
實務推薦 | 現代主流 | 僅維護舊碼 | 繼承 / 大量實例 |
| 瀏覽器分頁名稱 + 搜尋結果標題 | 對 SEO 影響大 |
| 載入外部 CSS | 瀏覽器讀到此行會暫停渲染,下載完才繼續 |
Share Dialog
Share Dialog
No activity yet