代碼:
function Box() {}
Box.prototype.name = 'Programmer';
Box.prototype.method = function() { return this.name; };
var box = new Box();
六個核心問題,一次講透。
| 問題 | 答案 |
|---|---|
| 1. box.__proto__ 訪問什麼? | Box.prototype 對象本身 |
| 2. box.method() vs box.__proto__.method() 差異? | 前者讀取原型方法,賦值會在實例創建遮蔽屬性;後者直接操作原型對象,影響所有實例 |
| 3. Box.prototype 是構造函數的原型對象嗎? | 是 |
| 4. 原型對象存儲在哪? | 堆內存,Box.prototype 指向它,所有實例 __proto__ 指向它 |
| 5. 實例原型的 __proto__ 指向哪? | Object.prototype → null (原型鏈終點) |
| 6. 構造函數 Box 的 __proto__ 指向哪? | Function.prototype → Object.prototype → null |
┌─────────────────────────────────────────────────────────────┐
│ Function.prototype │
│ (apply, call, bind, length, name, prototype: Object.prototype)│
└──────────────────────────┬──────────────────────────────────┘
│ [[Prototype]] (__proto__)
▼
┌─────────────────────────────────────────────────────────────┐
│ Box (函數對象) │
│ name: "Box" length: 0 prototype: ──────────────────────┐ │
└─────────────────────────────────────────────────────────────┘ │
│
│ .prototype (顯式原型)
▼
┌─────────────────────────────────────────────────────────────┐
│ Box.prototype (原型對象) │
│ constructor: Box name: "Programmer" method: ƒ() │
│ __proto__: ──────────────────────────────────────────────┐ │
└─────────────────────────────────────────────────────────────┘ │
│
│ [[Prototype]] (__proto__)
▼
┌─────────────────────────────────────────────────────────────┐
│ Object.prototype (根原型) │
│ constructor: Object toString: ƒ() hasOwnProperty: ƒ() ... │
│ __proto__: null (原型鏈終點) │
└─────────────────────────────────────────────────────────────┘
│
new Box() │
▼
┌─────────────────────────────────────────────────────────────┐
│ box (實例對象) │
│ __proto__: ──────────────────────────────────────────────┐ │
└─────────────────────────────────────────────────────────────┘ │
│
│ [[Prototype]] (__proto__)
▼
(指向 Box.prototype)
// 1. 核心關係
console.log(box.__proto__ === Box.prototype); // true
console.log(Box.prototype.__proto__ === Object.prototype); // true
console.log(Box.__proto__ === Function.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
// 2. 遮蔽效應演示
box.method = 12; // 實例新增自身屬性
console.log(box.method); // 12 (實例屬性)
console.log(box.__proto__.method); // ƒ() (原型方法未變)
console.log(Box.prototype.method); // ƒ()
Box.prototype.method = 1212; // 修改原型
console.log(box.method); // 12 (實例自身屬性遮蔽)
console.log(box.__proto__.method); // 1212
console.log(Box.prototype.method); // 1212
// 3. 新實例受原型修改影響
var box2 = new Box();
console.log(box2.method); // 1212 (讀取原型)
// 4. hasOwnProperty 區分自身/繼承屬性
console.log(box.hasOwnProperty('method')); // true (box 自己的)
console.log(box2.hasOwnProperty('method')); // false (繼承的)
class Box {
constructor() {}
method() { return this.name; }
}
static name = 'Programmer'; // 類靜態屬性 ≠ 原型屬性
// 編譯後本質:
// Box.prototype.method = function() { ... }
// Object.defineProperty(Box, 'name', { value: 'Programmer' })
box.__proto__ = otherProto 破壞繼承鏈,性能極差,已棄用const proto = { method() {} }; const obj = Object.create(proto);function Box() { return {a:1}; } new Box() → {a:1} (非 Box 實例)prototype,無 this 綁定掌握這張圖,JS 繼承機制就徹底懂了。