代碼:
function Box(name, age) {
this.name = name;
this.age = age;
this.method = function() { return 'hello'; };
}
var box = new Box('programmer', 25);
三個核心問題,配合圖解徹底講透。
Box 是函數對象。JS 中函數是一等公民,本質是 Function 構造函數創建的對象。變量 Box 指向這個函數對象的引用(指針)。
console.log(typeof Box); // "function"
console.log(Box instanceof Function); // true
console.log(Box.constructor === Function); // true
Box.prototype.country = 'China';
Box.prototype.race = 'Han';
執行時機:代碼解析階段立即執行,與是否實例化無關。
box 可能還不存在,但原型對象已經有了 country/racenew Box() 創建的實例,__proto__ 指向同一個已擴展的原型對象// 驗證:實例化前原型已有屬性
console.log(Box.prototype.country); // "China" (未 new 也能訪問)
var box1 = new Box('a', 1);
var box2 = new Box('b', 2);
console.log(box1.country); // "China" (繼承自原型)
console.log(box2.race); // "Han"
存儲位置:堆內存。Box.prototype 是一個普通對象,存儲在堆中。Box 函數對象持有指向它的引用 (.prototype 屬性)。所有實例通過內部槽 [[Prototype]] (暴露為 __proto__) 指向同一個對象。
┌─────────────────────────────────────────────────────────────┐
│ 堆內存 │
│ ┌──────────────┐ .prototype ┌────────────────────┐ │
│ │ Box (函數) │ ───────────────► │ Box.prototype │ │
│ │ │ │ { │ │
│ │ name: "Box" │ │ constructor: Box,│ │
│ │ length: 2 │ │ country: "China",│ │
│ │ prototype: ●──────────────────► │ race: "Han" │ │
│ └──────────────┘ │ } │ │
│ └─────────┬──────────┘ │
│ │ [[Prototype]] │
│ ▼ │
│ ┌────────────────────┐ │
│ │ Object.prototype │ │
│ │ { toString... } │ │
│ └─────────┬──────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ null (原型鏈終點) │ │
│ └────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
▲
│ __proto__ (內部槽)
│
┌─────────────────────────────────────────────┼────────────────┐
│ box1, box2 (實例) │ │
│ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ box1 │ │ box2 │ │ │
│ │ { │ │ { │ │ │
│ │ name: "a", │ │ name: "b", │ │ │
│ │ age: 1, │ │ age: 2, │ │ │
│ │ method: ƒ() │ │ method: ƒ() │ │ │
│ │ __proto__: ●───┼──┼──► Box.prototype │ │ │
│ │ } │ │ } │ │ │
│ └──────────────────┘ └──────────────────┘ │ │
└─────────────────────────────────────────────┘ │
// box1.country 查找過程:
1. box1 自身屬性?無
2. box1.__proto__ (Box.prototype) 有 country?有 → 返回 "China"
3. 結束
// box1.method 查找過程:
1. box1 自身屬性?有 (構造函數里 this.method = ƒ) → 返回 ƒ
2. 結束 (不再向上查找,實例屬性遮蔽原型同名屬性)
name, age, method):每個實例獨有,佔用各自內存country, race):所有實例共享一份,節省內存method 在構造函數定義 → 10000 個函數對象;定義在 prototype → 1 個函數對象function Box(name, age) {
this.name = name; // 實例屬性:每個實例不同
this.age = age;
}
// 方法、常量、默認值放原型
Box.prototype.sayHello = function() {
return 'Hello, ' + this.name;
};
Box.prototype.defaultAge = 18; // 默認值
Box.prototype.species = 'Human'; // 常量
// 避免在構造函數定義函數 (除非需閉包捕獲實例變量)
掌握這張圖,JS 面向對象本質就通了:構造函數管理實例差異,原型對象管理實例共性。