首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

JavaScript 構造函數與原型對象深度解析:prototype、__proto__、constructor 關係全圖譜

     編輯:程式設計綜合問答

代碼:

function Box(name, age) {
    this.name = name;
    this.age = age;
    this.method = function() { return 'hello'; };
}
var box = new Box('programmer', 25);

三個核心問題,配合圖解徹底講透。

問題 1:Box 是什麼?對象還是引用?

Box 是函數對象。JS 中函數是一等公民,本質是 Function 構造函數創建的對象。變量 Box 指向這個函數對象的引用(指針)。

console.log(typeof Box);           // "function"
console.log(Box instanceof Function); // true
console.log(Box.constructor === Function); // true

問題 2:prototype 添加屬性執行時機

Box.prototype.country = 'China';
Box.prototype.race = 'Han';

執行時機:代碼解析階段立即執行,與是否實例化無關。

  • JS 引擎解析腳本時,依次執行語句
  • 執行到這兩行時,立即在 Box.prototype 對象上創建屬性
  • 此時 box 可能還不存在,但原型對象已經有了 country/race
  • 後續 new 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"

問題 3:原型對象存儲位置與運行機制

存儲位置:堆內存。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):所有實例共享一份,節省內存
  • 若 10000 個實例,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 面向對象本質就通了:構造函數管理實例差異,原型對象管理實例共性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved