JS學習筆記#25 | 原型(Prototype)與原型鏈

更新於 發佈於 閱讀時間約 7 分鐘

一、簡介

  • 什麼是原型?
    每個建構函數都有 prototype 屬性,是一個物件,用來存放共享的方法或屬性。
  • 什麼是原型鏈?
    物件透過 __proto__ 連接到其原型,形成屬性和方法的查找路徑。


二、原型的基本運作

function Person(name, age) {

this.name = name;

this.age = age;

}

Person.prototype.sayHi = function() {

console.log(`Hi, I'm ${this.name}`);

};

const p1 = new Person("Alice", 25);

const p2 = new Person("Bob", 30);

p1.sayHi(); // "Hi, I'm Alice"

p2.sayHi(); // "Hi, I'm Bob"

console.log(p1.sayHi === p2.sayHi); // true(共用同一個方法)

記憶體優勢

如果每個實例都帶一個 sayHi 函數,會浪費記憶體,因為這些函數功能相同,只是 this 不同,而將方法定義在 Person.prototype 上,所有實例共享,就能節省記憶體。

  • 未優化時:p1p2 各有一份 sayHi,佔用不同記憶體位置(p1.sayHi !== p2.sayHi)。
  • 優化後:sayHi 只有一份,實例仍獨立,但方法共享。


三、原型鏈

new 創建實例時,實例的 __proto__ 內部屬性會自動設為建構函數的 prototype,形成查找鏈。

const p = new Person("Alice");

console.log(p.__proto__ === Person.prototype); // true

console.log(p.hasOwnProperty("sayHi")); // false(不在 p 自身)

console.log(p instanceof Person); // true

原型鏈結構

p.__proto__ -> Person.prototype

Person.prototype.__proto__-> Object.prototype

Object.prototype.__proto__-> null



查找過程:

訪問屬性或方法時,從實例自身開始,沿原型鏈向上查找。

1.p 是什麼?

p 是用 new Person("Alice") 創建的實例。

2.p.__proto__ 是什麼?

__proto__ 是每個物件都有的內部屬性,指向它的「原型」。

當你用 new Person 創建 p 時,JavaScript 自動設定:

p.__proto__ = Person.prototype

3.Person.prototype 是什麼?

它是 Person 建構函數的一個屬性,預設是個物件。

4.Person.prototype.__proto__ 是什麼?

Person.prototype 本身也是一個物件,所以它也有自己的 __proto__。

因為它是普通物件,它的 __proto__ 指向 Object.prototype

5.Object.prototype 是什麼?

它是 JavaScript 中所有物件的「老祖宗」,包含一些基本方法(如 toString、hasOwnProperty)。

預設由 Object 建構函數提供。

6.Object.prototype.__proto__ 是什麼?

Object.prototype 是鏈的頂端,它的 __proto__ 是 null。

表示「沒有更上面的原型了」,查找就停在這裡。


四、原型能放什麼?

prototype 既可以定義方法,也可以定義屬性,實例都能透過原型鏈繼承。

Person.prototype.species = "Human"; // 原型上的屬性

console.log(p1.species); // "Human"(從原型繼承)

console.log(p2.species); // "Human"

console.log(p1.species === p2.species); // true(共享)

為什麼較少用來放屬性?

原型上的屬性是共享的,若修改可能影響所有實例,不適合個別化的資料。

若屬性需個別值(如 name),應放實例自身。



五、繼承

function Animal(name) {
this.name = name;
}
Animal.prototype.eat = function() {
console.log(`${this.name} is eating`);
};

function Dog(name) {
this.name = name;
}
Dog.prototype = new Animal(); // 繼承:把 Dog 的原型設為 Animal 的實例
Dog.prototype.bark = function() {
console.log(`${this.name} says woof`);
};

const dog = new Dog("Max");
dog.eat(); // "Max is eating"(從 Animal 繼承)
dog.bark(); // "Max says woof"(Dog 自己的)

// 驗證
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true

步驟解析

  1. 父類 Animal:定義 name 屬性和 eat 方法。
  2. 子類 Dog:設置 Dog.prototype = new Animal(),讓 Dog 的原型變成一個 Animal 實例。
  3. 創建 dog:new Dog("Max") 生成實例,dog 有自己的 name。

原型鏈

dog.__proto__ -> Dog.prototypenew Animal() 的實例)Dog.prototype.__proto__ -> Animal.prototype

Animal.prototype.__proto__ -> Object.prototype


六、注意事項

1.屬性遮蔽:實例上的同名屬性會覆蓋原型上的屬性。

dog.eat = function() {
console.log("Dog-specific eating");
};
dog.eat(); // "Dog-specific eating"(遮蔽原型)


2.避免循環引用:確保原型鏈不會無限循環。例如: 若誤設循環引用(如 A.prototype.__proto__ = A),查找會無限循環。


avatar-img
0會員
28內容數
留言
avatar-img
留言分享你的想法!
koko的沙龍 的其他內容
建構函數是 JavaScript 中用來創建和初始化物件的一種特殊函數。它像一個「模具」,透過 new 關鍵字生成多個相似的物件實例。
遞迴是指一個函數自己呼叫自己,用來解決可以分解成相似小問題的大問題。它就像一層層深入,最後再一層層回來的過程。
調用棧是 JavaScript 引擎用來管理函數執行的一種資料結構。 它就像一個垂直的待辦清單,追踪當前正在執行的函數,以及它們的順序。 核心特點 遵循「後進先出」(LIFO, Last In First Out)的規則。 每個函數調用會被「推入」
閉包是指一個函數能夠「記住」它被創建時的外部環境(作用域),即使那個外部環境已經不存在了。 簡單來說,閉包就像是函數帶著一個「記憶背包」,裡面裝著它出生時能看到的變數。
作用域是指程式中變數的可訪問範圍,也就是變數在哪裡可以被存取。 JavaScript 有幾種作用域類型: 1.全域作用域(Global Scope) 變數在程式最外層定義,任何地方都可以存取。 var globalVar1 = "我是全域的1"; let globalV
什麼是提升?在 JavaScript 中,提升是指變數和函數宣告會在程式執行前被「提升」到它們所在作用域(scope)的頂部。這是 JavaScript 引擎處理程式碼時的一種行為,讓你在宣告之前就能使用某些變數或函數。
建構函數是 JavaScript 中用來創建和初始化物件的一種特殊函數。它像一個「模具」,透過 new 關鍵字生成多個相似的物件實例。
遞迴是指一個函數自己呼叫自己,用來解決可以分解成相似小問題的大問題。它就像一層層深入,最後再一層層回來的過程。
調用棧是 JavaScript 引擎用來管理函數執行的一種資料結構。 它就像一個垂直的待辦清單,追踪當前正在執行的函數,以及它們的順序。 核心特點 遵循「後進先出」(LIFO, Last In First Out)的規則。 每個函數調用會被「推入」
閉包是指一個函數能夠「記住」它被創建時的外部環境(作用域),即使那個外部環境已經不存在了。 簡單來說,閉包就像是函數帶著一個「記憶背包」,裡面裝著它出生時能看到的變數。
作用域是指程式中變數的可訪問範圍,也就是變數在哪裡可以被存取。 JavaScript 有幾種作用域類型: 1.全域作用域(Global Scope) 變數在程式最外層定義,任何地方都可以存取。 var globalVar1 = "我是全域的1"; let globalV
什麼是提升?在 JavaScript 中,提升是指變數和函數宣告會在程式執行前被「提升」到它們所在作用域(scope)的頂部。這是 JavaScript 引擎處理程式碼時的一種行為,讓你在宣告之前就能使用某些變數或函數。
你可能也想看
Google News 追蹤
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
※ TypeScript範例說明: interface ITest { test1: string test2: number print: (arg: string[]) => boolean } class Test implements ITest { public te
Thumbnail
自訂元件生成位置顧名思義就是可以指定部分HTML區塊渲染在特定的畫面上,即使在不同組件也能把A組件內的部分畫面,展現在B組件上,以下方程式舉例。
Thumbnail
JavaScript是一種具有動態型別、弱型別、原型繼承等特性的高級腳本語言,應用範圍廣泛,包括前端開發、後端開發、移動應用等。它被各種公司和開源社區廣泛使用。學習JavaScript需要掌握ECMAScript標準、異步編程、模塊系統等知識。
※ Object(物件) & Constructor Function(建構式函式) Object(物件)是什麼? 物件是一種「可以將資料、程式碼包含在其中」的資料結構。 Object(物件)的兩種創造方式: 匿名物件:直接使用"{}"。沒有特別的名字,直接從Object中繼承過來的一個物件
※ Javascript和HTML的關係 當我們輸入新的網址或按下重新整理時,從解析檔案到畫面顯示出來,瀏覽器會進行以下流程: 解析 HTML / CSS 檔案,建立物件模型: HTML → DOM (Document Object Model)。 CSS → CSSOM (CSS Obje
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
針對 JavaScript 中的原始型別和隱性轉型進行了詳細的探討
Thumbnail
前言 現在的前端需求已經越來越高,要考慮HTML及CSS的切版美觀程度,以及React以及Flutter所提出的元件(Componet、widget)觀念,也就是將元件模組化,使元件可以更動態的被程式運行,而不用靜態的客製化每一個介面。開發一個好的元件可以提升整體的開發速度,讓任何使用元件的開發者
Thumbnail
全方位分析脫離繼承戰的方法,大膽猜測誰會成為卡丁國下一任國王。
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
※ TypeScript範例說明: interface ITest { test1: string test2: number print: (arg: string[]) => boolean } class Test implements ITest { public te
Thumbnail
自訂元件生成位置顧名思義就是可以指定部分HTML區塊渲染在特定的畫面上,即使在不同組件也能把A組件內的部分畫面,展現在B組件上,以下方程式舉例。
Thumbnail
JavaScript是一種具有動態型別、弱型別、原型繼承等特性的高級腳本語言,應用範圍廣泛,包括前端開發、後端開發、移動應用等。它被各種公司和開源社區廣泛使用。學習JavaScript需要掌握ECMAScript標準、異步編程、模塊系統等知識。
※ Object(物件) & Constructor Function(建構式函式) Object(物件)是什麼? 物件是一種「可以將資料、程式碼包含在其中」的資料結構。 Object(物件)的兩種創造方式: 匿名物件:直接使用"{}"。沒有特別的名字,直接從Object中繼承過來的一個物件
※ Javascript和HTML的關係 當我們輸入新的網址或按下重新整理時,從解析檔案到畫面顯示出來,瀏覽器會進行以下流程: 解析 HTML / CSS 檔案,建立物件模型: HTML → DOM (Document Object Model)。 CSS → CSSOM (CSS Obje
※ 函式基礎介紹: ※ JavaScript 特殊的函式特性: 函式可以當成值來傳遞 (可以放進變數或放進物件) 函式可以當成函式的參數 callback - 在特定事件中觸發函式 (非同步特性) ※ 函式的基本寫法: ※ 調用 (invoke) 函式: "調用" 意指呼叫或執行
Thumbnail
針對 JavaScript 中的原始型別和隱性轉型進行了詳細的探討
Thumbnail
前言 現在的前端需求已經越來越高,要考慮HTML及CSS的切版美觀程度,以及React以及Flutter所提出的元件(Componet、widget)觀念,也就是將元件模組化,使元件可以更動態的被程式運行,而不用靜態的客製化每一個介面。開發一個好的元件可以提升整體的開發速度,讓任何使用元件的開發者