← JavaScript Learning Path

JAVASCRIPT V2 · EVERYDAY · 61

Class / Object Model:何時資料與行為值得放在同一個 Instance?

學到 class 不代表所有 object 都應該變成 class。這一課要比較三種設計:plain data + functions、factory/object、class instances。你要能說明 state、method、this、prototype 與 serialization trade-off,而不是只背 constructor。

Learning outcomes

1. Why now:有些 entity 真的需要「每個 instance 有 state + 共享 behavior」

const courseA = {
  title: "JS",
  score: 90
};

const courseB = {
  title: "Math",
  score: 55
};

如果很多 Course 都需要相同 isPassed() behavior,class 是一種組織方式;但不是唯一方式。

2. Class / Instance / State / Method

class Course {
  constructor(title, score) {
    this.title = title;
    this.score = score;
  }

  isPassed() {
    return this.score >= 60;
  }
}

const js =
  new Course("JavaScript", 90);
TermExample
classCourse
instancejs
instance statetitle / score
methodisPassed()

3. Worked Example A:不同 instances,各自保存 state

const js =
  new Course("JavaScript", 90);

const math =
  new Course("Math", 55);

js.isPassed();   // true
math.isPassed(); // false

method code 是共享行為,但 this.score 讀的是目前 call receiver 的 instance state。

4. this 不是「永遠自動指 class」

const check = js.isPassed;

check();
// call-site 改變,this 不一定是 js

JavaScript 的 this 取決於呼叫方式。這一課先建立警覺,130 會深入 prototype/this。

5. Class syntax 建立在 prototype model 上

console.log(
  Course.prototype.isPassed
);

一般 class method 不會每個 instance 都複製一份 function;它們透過 prototype mechanism 共享。現在不需要背 prototype chain 細節,但要知道 class 不是獨立於 JavaScript object model 的另一套世界。

6. Plain object + pure function 仍然很強

const student = {
  name: "Amy",
  score: 92
};

function isPassed(student) {
  return student.score >= 60;
}

API payload、DTO、configuration 等資料常常用 plain object 更直接,也更容易 JSON serialize。

7. Worked Example B:同一需求的兩種設計

// Data + function
const course = {
  title: "JS",
  score: 90
};

isPassed(course);

// Class
const course2 =
  new Course("JS", 90);

course2.isPassed();

沒有「class 比 object 高級」。選擇取決於 behavior/invariant 是否真的和 instance identity 綁得很緊。

8. Constructor 可以建立 invariant,但不要藏太多 side effects

class Course {
  constructor(title, score) {
    if (!title) {
      throw new Error("title required");
    }

    this.title = title;
    this.score = score;
  }
}

Constructor 適合建立有效初始 state;不宜隨便塞 network request、DOM 操作等難以預期的 side effects。

9. Serialization boundary:JSON 不會保存 method behavior

const raw =
  JSON.stringify(
    new Course("JS", 90)
  );

const restored =
  JSON.parse(raw);

restored 只有 data properties,不會自動恢復成 Course instance/method。這就是 object model 與 transport representation 的 boundary。

10. Inheritance 不是 class 的必修終點

Entity
  → Person
  → Student
  → OnlineStudent
  → PremiumOnlineStudent

過深 inheritance 會把 behavior 耦合在 hierarchy。很多情況 composition + plain functions 更清楚。

11. Worked Example C:Composition

function createCourse(
  data,
  gradingPolicy
) {
  return {
    ...data,
    isPassed() {
      return gradingPolicy(
        this.score
      );
    }
  };
}

這不是要求你改用 factory;只是要看到「共享 behavior」不只 inheritance 一種解法。

12. Common mistakes

A. 所有 API data 都轉 class

增加 mapping/serialization 成本,未必有價值。

B. method extraction 後以為 this 仍自動綁 instance

call-site 會影響 this。

C. constructor 做太多 I/O

建立 object 變得難測、難預期。

D. 用 inheritance 表達其實只是「有某個能力」

composition 往往更靈活。

13. Debug evidence:確認 instance / prototype / call-site

console.log(
  js instanceof Course
);

console.log(
  Object.getPrototypeOf(js)
    === Course.prototype
);

如果 method 行為怪異,也要看它是怎麼被呼叫,而不是只看 method 定義。

14. Guided exercise:兩個 instances

const a =
  new Course("A", 60);

const b =
  new Course("B", 59);

預測 a/b 的 isPassed(),再說明 this.score 每次讀的是哪個 state。

15. Independent exercise:實作 Course.summary()

Course 有 title/score;isPassed() 判斷 60;summary() 回傳 JS:Pass 或 Math:Fail。多個 instances 都要成立。

16. Challenge:選設計並寫理由

三種資料:API response DTO、DOM button component state、bank account domain entity。哪些適合 plain object + pure function,哪些可能值得 class?理由必須談 behavior/invariant/serialization,而不是「class 比較專業」。

Project checkpoint:Grade Tracker Design Decision

Data + pure functions

容易 JSON / storage / testing;適合 Grade Tracker 現有資料流。

Course class

若 Course 有長期 instance behavior/invariant,class 可能更自然。

目前 Grade Tracker 預設仍採 plain Student objects + pure functions,因為資料需要頻繁 serialize、filter、map。Class 只作為可理解的設計選項,不強制套用。

17. Vocabulary / Summary

18. Further reading

Knowledge check

  1. 區分 class、instance、state、method。
  2. 說明 method extraction 為何可能影響 this。
  3. 解釋 JSON round trip 為何不會保存 class methods。
  4. 比較 plain object + pure function 與 class 的 trade-off。
  5. 完成 Course.summary() mastery tests。