Day 61:JavaScript 物件導向;HTML 骨架元素

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

課程講義

YouTube 影片播放清單

📅 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

兩者的根本差異在於繼承機制:傳統 OOP 繼承的是「結構 + 行為」,JavaScript 的原型鏈則可以連「狀態」一起繼承。 developer.mozilla

機制

傳統 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


建構子函式(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

  • 支援 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

  • 支援 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


五、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

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

三種模式完整對照

工廠函式

建構子函式

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

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

<html lang="zh-TW">

文件根元素,宣告語言 developer.mozilla

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

<meta charset="UTF-8">

宣告 UTF-8 編碼,支援中文與 emoji stackoverflow

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

<meta name="viewport" ...>

行動裝置適配指令 developer.mozilla

缺少此行,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

類型

前後換行

可設寬高

代表元素

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


延伸閱讀