JavaScript 是一種基於原型的語言,理解原型(prototype)和原型鏈(prototype chain)對於深入掌握 JavaScript 的物件導向程式設計非常重要。本文將詳細解釋什麼是原型、原型鏈以及隱式原型,並透過示例程式碼來展示它們的工作原理。
原型
在 JavaScript 中,每個函式都有一個 prototype
屬性,這個屬性是一個物件,包含了由該函式建立的例項物件的公共祖先。當透過建構函式建立一個物件時,例項物件會繼承建構函式 prototype
物件上的屬性和方法。
// 定義一個建構函式 function Person(name) { this.name = name; } // 在 Person 的 prototype 上定義一個方法 Person.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name}`); }; // 透過建構函式建立例項物件 const person1 = new Person('Alice'); const person2 = new Person('Bob'); // 呼叫例項物件的方法 person1.sayHello(); // 輸出:Hello, my name is Alice person2.sayHello(); // 輸出:Hello, my name is Bob
在這個示例中,sayHello
方法被定義在 Person
建構函式的 prototype
上,因此所有透過 Person
建構函式建立的例項物件都可以訪問這個方法。
原型鏈
當我們訪問物件的屬性或方法時,JavaScript 引擎會先在物件自身查詢。如果沒有找到,它會沿著原型鏈向上查詢,直到找到屬性或方法,或者到達原型鏈的頂端 null
為止。這個鏈狀的查詢過程被稱為原型鏈。
// 定義一個建構函式 function Animal(type) { this.type = type; } Animal.prototype.eat = function() { console.log(`${this.type} is eating`); }; // 定義另一個建構函式,並將其原型設定為 Animal 的例項 function Dog(name) { Animal.call(this, 'Dog'); this.name = name; } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.bark = function() { console.log(`${this.name} is barking`); }; // 建立 Dog 的例項 const dog = new Dog('Buddy'); // 呼叫例項物件的方法 dog.eat(); // 輸出:Dog is eating dog.bark(); // 輸出:Buddy is barking
在這個示例中,Dog
的原型被設定為 Animal
的例項,因此 Dog
的例項物件可以訪問 Animal
原型上的 eat
方法。這個查詢過程就是原型鏈的實際應用。
將程式碼放到瀏覽器我們可以透過控制檯我們可以檢視它的原型查詢過程,直到找到object的隱式原型為空原型鏈的查詢就到頭了
隱式原型
每個 JavaScript 物件都有一個 __proto__
屬性,這個屬性指向建立該物件的建構函式的 prototype
。透過 __proto__
屬性,可以實現原型鏈的查詢。
// 檢查 dog 物件的隱式原型 console.log(dog.__proto__ === Dog.prototype); // 輸出:true console.log(Dog.prototype.__proto__ === Animal.prototype); // 輸出:true console.log(Animal.prototype.__proto__ === Object.prototype); // 輸出:true console.log(Object.prototype.__proto__ === null); // 輸出:true
在這個示例中,我們可以看到 dog
物件的 __proto__
屬性指向 Dog
建構函式的 prototype
,而 Dog.prototype.__proto__
指向 Animal.prototype
,最終 Animal.prototype.__proto__
指向 Object.prototype
,Object.prototype.__proto__
為 null
,表示原型鏈的終點。
所有物件都有原型嗎?
並不是所有的物件都有原型。透過 Object.create(null)
建立的物件沒有原型。
// 建立一個沒有原型的物件 const obj = Object.create(null); console.log(obj.__proto__); // 輸出:undefined console.log(Object.getPrototypeOf(obj)); // 輸出:null
在這個示例中,我們建立了一個沒有原型的物件 obj
,因此 obj.__proto__
為 undefined
,Object.getPrototypeOf(obj)
返回 null
。
結論
透過理解原型、原型鏈和隱式原型,我們可以更好地掌握 JavaScript 的物件導向程式設計。這些概念幫助我們理解物件之間的繼承關係,以及如何透過原型鏈實現方法和屬性的共享。希望這篇文章和示例程式碼能幫助你更深入地理解 JavaScript 中的原型機制。
最後:
大家可以透過分析一下這張圖來加深印象