JAVASCRIPT V2 · ADVANCED · 130
this / Prototype / Class:Object Model 不是「Class 語法背熟」
61 先讓你會用 class;這一課往下拆 model:property lookup 如何走 prototype chain、method function 如何共享、this 為什麼主要由 call-site 決定,以及 method 被拆出來後 context 為何會消失。
Learning outcomes
- 理解 own property 與 prototype chain lookup。
- 能預測 common method-call forms 的 this。
- 理解 detached method、bind 與 arrow lexical this。
- 知道 class syntax 與 prototype model 的關係。
- 能比較 class、plain object、closure factory 的 state/behavior trade-offs。
1. Why now:Class 很好用,但 bug 往往發生在它下面
class Student {
summary() {
return this.name;
}
}
const amy =
new Student();
amy.name = "Amy";
const fn = amy.summary;
fn();
如果你只背「class 裡 method 用 this」,你很難解釋 detached method 為何失去原 receiver。
2. Mental model:Property Lookup 沿 Prototype Chain
amy
own:
name = "Amy"
[[Prototype]]
Student.prototype
summary()
[[Prototype]]
Object.prototype
當 amy.summary 在 own properties 找不到時,engine 會沿 prototype chain 往上找。
3. Worked Example A:Methods 通常共用同一 Function
class Student {
summary() {
return this.name;
}
}
const a =
new Student();
const b =
new Student();
console.log(
a.summary
=== b.summary
);
// true
方法通常在 Student.prototype,不是每個 instance 各複製一份 method function。
4. this 看 Call-Site
const student = {
name: "Amy",
show() {
return this.name;
}
};
student.show();
// this = student
對一般 method call,點號左側 receiver 會影響 this binding。
5. Worked Example B:Detached Method
const show =
student.show;
show();
現在 call-site 已不再是 student.show()。在 strict/module context 中 this 可能是 undefined,因此讀 this.name 會失敗。
6. bind:建立固定 this 的新 Function
const boundShow =
student.show.bind(
student
);
boundShow();
// "Amy"
bind 不會立刻執行原 function;它回傳一個新 function,之後呼叫時使用固定 context。
7. Arrow Function:沒有自己的 this
const obj = {
name: "Amy",
bad: () => {
return this.name;
}
};
Arrow function 的 this 來自 lexical surrounding context;因此它通常不適合拿來當需要 receiver-based this 的 object method。
8. Worked Example C:Arrow 在 Method 裡捕捉外層 this
const student = {
name: "Amy",
delayed() {
setTimeout(
() => {
console.log(
this.name
);
},
0
);
}
};
student.delayed();
這裡 arrow callback 使用 delayed method 的 lexical this,因此能保留 student receiver。
9. prototype 不是 class 專屬
function Student(name) {
this.name = name;
}
Student.prototype.summary =
function () {
return this.name;
};
const amy =
new Student("Amy");
class syntax 讓 prototype-based construction 更易讀,但底層 method lookup 仍建立在 prototype chain。
10. Common mistakes
A. 認為 this 永遠指向「定義 method 的 object」
一般 function 的 this 看呼叫方式。
B. 把所有 methods 改 arrow
這會改變 this model,也可能讓每個 instance 各持有 function property。
C. 修改 built-in prototype 當便利擴充
可能造成名稱衝突與全域行為污染。
D. 把 prototype chain 當 inheritance tree 的完整替代說法
先理解 property delegation,再談 class inheritance 語法。
11. Debug evidence:印 call-site 與 receiver
console.log(
student.show
=== show
);
// same function
student.show();
// receiver exists
show();
// receiver removed
Function identity 沒變,呼叫形式變了。這是 detached method bug 的第一個重要 evidence。
12. Guided exercise:修復 callback context
const button = {
label: "Save",
click() {
console.log(
this.label
);
}
};
setTimeout(
button.click,
0
);
分別用 wrapper 與 bind 修復,並解釋兩者何時建立 function、this 從哪裡來。
13. Independent exercise:Prototype Lookup Trace
建立 class Course,instance 有 own property title,prototype 有 summary。用 Object.hasOwn 與 Object.getPrototypeOf 證明 title 與 summary 分別在哪一層。
Object.hasOwn(
course,
"title"
);
Object.hasOwn(
course,
"summary"
);14. Challenge:Class vs Closure Factory
分別用 class 與 closure factory 做 counter。比較 state visibility、method sharing、private-like state 與 this dependency。沒有唯一勝者;重點是 trade-off。
Project checkpoint:Grade Tracker Course Model
class Course {
constructor(
title,
score
) {
this.title = title;
this.score = score;
}
summary() {
return (
this.title
+ ": "
+ this.score
);
}
}
把 summary 傳給 array/timer/event 前,必須重新檢查 callback contract 與 this context。
15. Vocabulary / Summary
- own property:直接存在 object 本身的 property。
- prototype chain:找不到 property 時的 delegation chain。
- receiver:method call 中接收 method 的 object。
- detached method:從 object 取出後獨立呼叫的 function。
- bind:建立固定 this/部分參數的新 function。
- lexical this:arrow function 從外層取得 this。
16. Further reading
Knowledge check · Mastery
- 畫出 instance → prototype → Object.prototype。
- 解釋 detached method 為何失去 receiver。
- 完成 bind/wrapper 修復與 prototype trace。
- 完成頁面下方 Executable JS this/prototype tests。