Skip to content

2026-06-27

/ 18 分鐘閱讀

/ Deep JavaScript Foundations v3

原型鏈:物件之間的連結機制

本篇延續上一篇對「連結 vs 複製」的討論。上一篇確立了 JavaScript 的原型系統是連結操作,而非複製操作。本篇逐步建立原型系統的完整物件關係圖,說明 Object.prototypeprototype 屬性、new 的連結行為,以及原型鏈的查找機制。

程式執行前的世界

在任何程式碼執行之前,JavaScript 引擎就已經建立了幾個關鍵實體。

首先是 Object 函式:這是內建的基礎函式,也是 Object.keysObject.values 等工具方法的命名空間。

與它並存的是 Object.prototype:一個極其重要的物件,是 JavaScript 中所有非原始型別的最終原型。toStringvalueOf 等基礎方法都存在於此。

這兩者之間有兩個屬性互相指向:

  • Object.prototype 屬性:從 Object 函式指向 Object.prototype 物件
  • constructor 屬性:從 Object.prototype 指回 Object 函式

定義函式時發生的事

javascript
function Workshop(teacher) {
  this.teacher = teacher;
}
Workshop.prototype.ask = function(question) {
  console.log(this.teacher, question);
};

當第 1 行宣告 Workshop 函式時,JavaScript 同時建立了兩個實體:

  1. Workshop 函式本身
  2. 一個空物件,稱為 Workshop.prototype

兩者之間同樣存在雙向的屬性:

  • Workshop.prototype 屬性:從 Workshop 函式指向 Workshop.prototype 物件
  • constructor 屬性:從 Workshop.prototype 指回 Workshop 函式

此外,Workshop.prototypeObject.prototype 之間存在一條隱藏的連結(後文的 dunderproto 會說明這條連結)。

第 4 行在 Workshop.prototype 上新增了 ask 方法。此時 Workshop.prototype 物件上有了一個 ask 屬性。

new 的四個步驟與連結的建立

接著我們執行以下程式碼:

javascript
var deepJS = new Workshop("Kyle");
var reactJS = new Workshop("Suzy");

new Workshop("Kyle") 為例,new 依序執行四件事:

  1. 建立一個全新的空物件
  2. 將這個新物件連結到 Workshop.prototype(這就是那條隱藏的連結)
  3. 以這個新物件作為 this,呼叫 Workshop 函式,執行 this.teacher = teacher,將 teacher 屬性直接寫在新物件上
  4. 回傳這個新物件,賦值給 deepJS

完成後,deepJS 是一個只有 teacher 屬性的物件,它本身沒有 ask 方法。reactJS 也是同樣的過程,只是 teacher 值不同。

原型鏈的查找

我們透過前面產生出來的 deepJS 來嘗試執行 ask 方法:

javascript
deepJS.ask("Is 'prototype' a class?");
// Kyle Is 'prototype' a class?

deepJS 上沒有 ask 屬性。JavaScript 不會就此停止,而是沿著連結往上查找:

  1. deepJS 上找不到 ask
  2. 沿著隱藏連結走到 Workshop.prototype
  3. Workshop.prototype 上找到了 ask

找到 ask 之後,呼叫它時 this 的值由呼叫點決定。呼叫點是 deepJS.ask(...),因此 this 指向 deepJSthis.teacher 就是 deepJS 上的 "Kyle"

這正是原型系統的核心優勢:一個方法定義在 Workshop.prototype 上,可以被無數個實例共享,而每次呼叫時 this 都正確指向各自的實例。

constructor 的本質

接著說明 constructor 屬性:

javascript
deepJS.constructor === Workshop; // true

deepJS 上沒有 constructor 屬性。JavaScript 沿原型鏈查找,到 Workshop.prototype 時找到了 constructor,它指向 Workshop 函式。

這讓 deepJS.constructor === Workshop 成立,看起來像是 deepJS 是由 Workshop 「建構」的。但這只是一種敘事上的安排,用來維持類別系統的假象,並非真正有意義的連結。

Dunderproto:存取原型鏈的入口

javascript
deepJS.__proto__ === Workshop.prototype; // true
Object.getPrototypeOf(deepJS) === Workshop.prototype; // true

__proto__ 讀作 dunderproto(double underscore proto 的縮寫)。它的外觀像是一個屬性,但實際上是定義在 Object.prototype 上的一個 getter 函式

當存取 deepJS.__proto__ 時,發生的事情是:

  1. deepJS 上沒有 __proto__ 屬性
  2. 沿原型鏈往上找到 Workshop.prototype,也沒有
  3. 繼續往上找到 Object.prototype,找到了一個名為 __proto__ 的 getter 函式
  4. 呼叫這個 getter,呼叫點是 deepJS,所以 this 指向 deepJS
  5. getter 從 this(也就是 deepJS)內部讀取隱藏的原型連結,回傳 Workshop.prototype

這裡 this 的綁定規則一樣適用,即使沒有括號,getter 的呼叫仍然是一次函式呼叫,this 由呼叫點決定。

Object.getPrototypeOf(deepJS) 是存取相同連結的標準方式,結果等同於 dunderproto,但語意更清晰。

複習

Object.prototype 在 JavaScript 中的用途是什麼?

所有非原始型別最終都連結到 Object.prototype,它包含 toStringvalueOf 等基礎工具方法,是整個語言物件體系的根基。

JavaScript 中的原型鏈(prototype chain)是什麼?

原型鏈是物件之間的內部連結機制。當在一個物件上找不到某個屬性時,JavaScript 會沿著連結往上查找下一個物件,直到找到為止。

在原型鏈上找到方法後呼叫它時,this 如何決定?

this 由呼叫點決定,而非由方法被找到的位置決定。即使方法是從原型鏈上找到的,呼叫它時 this 仍指向發起呼叫的物件。

JavaScript 原型系統中的 constructor 屬性有什麼作用?

constructor 是一個命名上的安排,從 prototype 物件指回建立它的函式,目的是製造類別系統存在的假象,在原型系統中並無更深的語意意義。

deepJS.constructor 指向什麼?

指向 Workshop,因為 Workshop.prototype 上有 constructor 屬性,而它指向 Workshop 函式。

__proto__ 的別名是什麼?

dunderproto(double underscore proto 的縮寫)。

存取 deepJS 的 dunderproto 時,JavaScript 會依序在哪裡查找?

先在 deepJS 本身,找不到後到 Workshop.prototype,再找不到才到 Object.prototype

Object.prototype.__proto__ 實際上是什麼?

一個 getter 函式,不是普通的資料屬性。

呼叫 __proto__ 的 getter 時,this 的上下文由什麼決定?

由呼叫點決定,在 deepJS.__proto__ 的情況下,呼叫點是 deepJS,所以 this 指向 deepJS

小測驗

JavaScript 中 `prototype` 屬性的主要用途是什麼? 為物件繼承建立一個參考連結點
在 JavaScript 中使用 `new` 關鍵字呼叫函式時,會發生什麼事? 建立空物件、連結到另一個物件、以 `this` 指向新物件呼叫函式、並回傳該物件
JavaScript 中的原型鏈是什麼? 透過連結的原型逐層查找屬性的機制
呼叫一個方法時,`this` 的值由什麼決定? 方法的呼叫點
JavaScript 原型系統中 `constructor` 屬性的用途是什麼? 製造類別系統存在的假象
double underscore proto 屬性的術語名稱是什麼? dunderproto
存取物件上不存在的屬性時,JavaScript 接下來會去哪裡找? 物件的原型
`constructor` 屬性通常指向什麼? 建立該物件的函式
透過 dunderproto 存取屬性時,實際上發生了什麼? 呼叫了一個 getter 函式

此文章是 FrontendMasters 上的 Deep JavaScript Foundations, v3 課程筆記

最後更新時間:

Buy Me A Coffee

系列章節 第 65 篇 / 共 66 篇

0 %
MIT Licensed | Copyright © 2025-present Wen-Hsiu's Blog
Photo by Federica Galli on Unsplash