# Day 61：JavaScript 物件導向；HTML 骨架元素

By [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake) · 2026-02-24

---

2/16 - 2/20 農曆年假休息，少量看台大 Vibe coding Web app 開發開放式課程

[課程講義](https://ric2k1.notion.site/114-1-NTU-Web-Programming-2590e6ef61828035b34dc965adc04382)

[YouTube 影片播放清單](https://www.youtube.com/playlist?list=PLIAzIZzCjtLJQzoK-1n6R_KVTXm4tHZqP)

> 📅 2026-02-23　｜　🏷 JavaScript · OOP · HTML · CSS

* * *

一、JavaScript 不是「傳統 OOP」
-----------------------

JavaScript 採用**原型導向（prototype-based）**，而非 Java、C++ 那種**類別導向（class-based）**。ECMAScript 規格書明確指出：

> _"ECMAScript objects are not fundamentally class-based such as those in C++, Smalltalk, or Java."_ [tc39](https://tc39.es/ecma262/)

兩者的根本差異在於繼承機制：傳統 OOP 繼承的是「結構 + 行為」，JavaScript 的原型鏈則可以連「狀態」一起繼承。 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes)

機制

傳統 OOP（Java / C++）

JavaScript

狀態由誰攜帶

實例（instance）

物件本身

方法由誰攜帶

類別（class）

物件本身

繼承的是什麼

結構 + 行為

結構 + 行為 + 狀態

繼承機制

類別繼承

`[[Prototype]]` 鏈

* * *

二、OOP 七個核心術語（汽車工廠比喻）
--------------------

術語

白話解釋

汽車比喻

類別（class）

設計藍圖

工廠設計圖紙

實例（instance）

照藍圖造出的實體

真正生產的一台車

狀態（state）

物件目前持有的資料

油量、顏色、時速

行為（behavior）

物件能做的事

加速、剎車、開燈

方法（method）

行為的程式碼實作

`accelerate()` 函式

結構（structure）

物件有哪些屬性名稱

規格表欄位

繼承（inheritance）

子類別自動獲得父類別的一切

電動車沿用汽車設計

* * *

三、三種建立物件的方式
-----------

### 工廠函式（Factory Function）

    // 直接 return 物件，呼叫時不需要 new
    const createCar = (brand, color) => ({
      brand,
      color,
      drive() {
        return `${brand} 行駛中`; // 透過閉包存取參數，不需要 this
      },
    });
    
    const bmw = createCar('BMW', 'red');
    

*   **不需要** `new`，呼叫方式與普通函式相同
    
*   方法各自獨立，每個實例自己持有一份（記憶體消耗較高）
    
*   無 `prototype` 鏈，`instanceof` 無效
    
*   閉包天然封裝狀態，不依賴 `this`，不會有 `this` 跑掉的問題 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Closures)
    

* * *

### 建構子函式（Constructor Function）

    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](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes)
    
*   支援 `instanceof` 型別判斷
    
*   **實務建議：新專案改用** `class`**，此寫法僅需看懂 legacy code**
    

* * *

### Class（ES6+）

    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](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript)
    
*   支援 `extends` 繼承，語法簡潔直觀
    
*   **自動開啟嚴格模式（strict mode）**，這是與建構子函式的重要差異
    
*   私有欄位 `#` 是 ES2020 真正新增的語言能力，並非語法糖
    

> ✅ **「class 是語法糖」大部分正確，但 ES2020 私有欄位** `#` **是例外。**

* * *

四、`this` 的運作邏輯
--------------

`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
    

### `this` 值依呼叫方式對照

呼叫方式

執行環境

`this` 結果

`bmw.drive()`

任何模式

`bmw`（呼叫的物件）

`fn()` 普通呼叫

非嚴格模式

`window`（自動替換）

`fn()` 普通呼叫

**嚴格模式 / class**

`undefined`（不替換）

**設計理念**：寧可立刻看到 `TypeError`，也不要讓 `this` 默默污染 `window`。 [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript)

* * *

五、TypeScript 的防護網
-----------------

TypeScript 的核心價值：**把執行期才會爆的錯誤，提前到編譯期攔截**。

### `this` 型別標註

    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` + 箭頭函式宣告的理由

**防止意外覆寫**：`const` 確保函式參考不被覆蓋，`function` 宣告式則沒有這個保護。 [stackoverflow](https://stackoverflow.com/questions/8698726/constructor-function-vs-factory-functions)

    const createCar = (brand) => ({ brand });
    
    createCar = () => {}; // ❌ TypeError 立刻報錯
    

**沒有自己的** `this`：箭頭函式從語法層面斷掉 `this`，與工廠函式「不依賴 `this`」的設計一致。

⚠ **TDZ 限制**：`const` 沒有 Hoisting，宣告前呼叫會拋出 `ReferenceError`。解法：把工廠函式的定義放在呼叫之前。

### 三種宣告方式對照

  

`const` + 箭頭函式

`const` + function 表達式

`function` 宣告式

防止意外覆寫

✅

✅

❌

定義前可呼叫（Hoisting）

❌ TDZ 報錯

❌ TDZ 報錯

✅

有自己的 `this`

❌（工廠函式不需要）

✅

✅

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](https://stackoverflow.com/questions/8698726/constructor-function-vs-factory-functions)

### 三種模式完整對照

  

工廠函式

建構子函式

Class

需要 `new`

❌

✅

✅

方法共用 prototype

❌ 各自一份

✅

✅

支援 `instanceof`

❌

✅

✅

支援 `extends`

❌

手動且複雜

✅

私有欄位 `#`

❌（用閉包代替）

❌

✅

依賴注入友善

✅

❌

較麻煩

實務推薦

✅ 現代主流

❌ 僅維護舊碼

✅ 繼承 / 大量實例

* * *

八、HTML 骨架解析
-----------

### Emmet 產生的 Boilerplate

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

標籤 / 屬性

作用

重要備注

`<!DOCTYPE html>`

宣告 HTML5 標準模式解析 [stackoverflow](https://stackoverflow.com/questions/52757069/doctype-vs-charset-in-html-5/52757163)

缺少會進入「怪異模式」，CSS 排版出錯

`<html lang="zh-TW">`

文件根元素，宣告語言 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata)

影響螢幕閱讀器、SEO、翻譯提示

`<meta charset="UTF-8">`

宣告 UTF-8 編碼，支援中文與 emoji [stackoverflow](https://stackoverflow.com/questions/4696499/meta-charset-utf-8-vs-meta-http-equiv-content-type)

必須放在 `<head>` 最前 1024 bytes 內

`<meta name="viewport" ...>`

行動裝置適配指令 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata)

缺少此行，RWD 全部失效

`<title>`

瀏覽器分頁名稱 + 搜尋結果標題

對 SEO 影響大

`<link rel="stylesheet">`

載入外部 CSS

瀏覽器讀到此行會暫停渲染，下載完才繼續

#### viewport 屬性拆解

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    

*   `width=device-width` — 版面寬度 = 裝置螢幕實際寬度
    
*   `initial-scale=1.0` — 初始縮放比例 1:1，不放大也不縮小
    

* * *

九、HTML 元素預設 display 類型
----------------------

三種核心行為決定元素如何在頁面流中排列： [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes)

類型

前後換行

可設寬高

代表元素

`block`

✅ 強制換行，撐滿父容器

✅

`<div>`, `<p>`, `<h1>`～`<h6>`

`inline`

❌ 不換行，跟著文字流

❌

`<span>`, `<a>`, `<strong>`

`inline-block`

❌ 不換行

✅

`<img>`, `<input>`, `<button>`

### 特殊 display 值

元素

預設 display

說明

`<li>`

`list-item`

類似 block，但有 `::marker` 項目符號

`<table>`

`table`

外部像 block，內部有表格排版邏輯

`<script>`, `<style>`

`none`

不渲染在畫面上

### ⚠ 常見易錯點

1.  **對** `inline` **元素設** `width`**/**`height` **無效** — 需先改成 `display: inline-block` 或 `block`
    
2.  `<img>` **底部有神秘空白** — 因為是 `inline-block`，會基線對齊；解法：`display: block` 或 `vertical-align: bottom`
    
3.  `<li>` **改成** `block` **會讓項目符號消失** — `list-item` 多了 `::marker` 虛擬元素
    
4.  `lang` **屬性記得改** — 中文網站用 `lang="zh-TW"`，影響無障礙、SEO 與翻譯功能 [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata)
    

* * *

延伸閱讀
----

*   [MDN — 繼承與原型鏈](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain) [developer.mozilla](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain)
    
*   [MDN — 物件原型](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes) [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes)
    
*   [MDN — JavaScript Classes](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript) [developer.mozilla](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Classes_in_JavaScript)
    
*   [MDN — What's in the head?](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata) [developer.mozilla](https://developer.mozilla.org/zh-TW/docs/Learn_web_development/Core/Structuring_content/Webpage_metadata)
    
*   [ECMAScript 2026 規格書](https://tc39.es/ecma262/) [tc39](https://tc39.es/ecma262/)

---

*Originally published on [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake/day-61)*
