跳至主要内容

請描述原型繼承如何在 JavaScript 運作中?

原型 (Prototype)

在 JavaScript 中,每個物件都有一個內部屬性稱為 [[Prototype]],它指向該物件的原型。原型是一個物件,包含了可以被其他物件共享的屬性和方法。

__proto__[[Prototype]]

  • __proto__:是大多數現代瀏覽器中暴露出的屬性,允許開發者存取和設置物件的原型(不建議直接使用),__proto__目前已經棄用了。
  • [[Prototype]]:描述了物件與其原型之間的隱藏式連結。

原型鏈(Prototype Chain)

原型鏈有往上查找的特性,當存取一個物件的屬性或方法時,Javascript 會先在物件本身尋找,如果找不到就會沿著原型鏈往上尋找,直到找到為止。原型鏈是由一系列物件組成的,它們通過 proto 屬性彼此相連,最終指向 Object.prototype,原型鏈的末端。

//定義一個建構子
function Phone(brand, model) {
this.brand = brand;
this.model = model;
}
//在 Phone.prototype 上定義一個方法,所有 Phone 的實例都能訪問
phone.prototype.ring = function () {
console.log("Ring ring!");
};
//使用建構子建立一個物件
const myPhone = new Phone("Apple", "iPhone 16");

//存取物件的屬性
console.log(myPhone.brand); // Apple

//存取從原型繼承的方法
myPhone.ring(); // Ring ring!

//原型鏈
// myPhone.__proto__ 的原型是 Phone.prototype
console.log(myPhone.__proto__ === Phone.prototype); // true

// Phone.prototype 的原型是 Object.prototype
console.log(Phone.prototype.__proto__ === Object.prototype); // true

// Object.prototype 是原型鏈的終點,其原型為 null
console.log(Object.prototype.__proto__ === null); // true

在這個範例中,myPhone 是 Phone 建構函數的實例,它的 proto 屬性指向 Phone.prototype,這樣它可以存取 Phone.prototype 上的 ring 方法。如果 Phone.prototype 上也找不到某個屬性或方法,JavaScript 會繼續沿著原型鏈向上查找,直到達到 Object.prototype

JavaScript 中的原型繼承是基於物件之間的關聯,每個物件都有一個原型物件,通過原型鏈的機制可以實現屬性和方法的繼承,可以實現一個物件從另一個物件繼承屬性和方法,實現程式碼的重用和擴展。


實例練習 1

48. Prototype

// This is a JavaScript Quiz from BFE.dev

function Foo() {}
Foo.prototype.bar = 1;
const a = new Foo();
console.log(a.bar);

Foo.prototype.bar = 2;
const b = new Foo();
console.log(a.bar);
console.log(b.bar);

Foo.prototype = { bar: 3 };
const c = new Foo();
console.log(a.bar);
console.log(b.bar);
console.log(c.bar);

解題1

function Foo() {}
Foo.prototype.bar = 1;
const a = new Foo();
console.log(a.bar); //1,Foo.prototype.bar 定義了一個屬性 bar,a 繼承了這個屬性。

Foo.prototype.bar = 2;
const b = new Foo();
console.log(a.bar); //2,Foo.prototype.bar 被重新賦值為 2,a 和 b 都繼承了這個屬性。
console.log(b.bar); //2,由於 a 的原型鏈指向 Foo.prototype,所以 a.bar 也變成了 2。

Foo.prototype = { bar: 3 };
const c = new Foo();
console.log(a.bar); //2,a 的原型鏈仍然指向舊的 Foo.prototype,所以 a.bar 仍然是 2。
console.log(b.bar); //2,b 的原型鏈仍然指向舊的 Foo.prototype,所以 b.bar 仍然是 2。
console.log(c.bar); //3,Foo.prototype 被重新賦值為一個新物件,c 繼承了這個新物件。

實例練習 2

53. Prototype 2

// This is a JavaScript Quiz from BFE.dev

function F() {
this.foo = "bar";
}

const f = new F();
console.log(f.prototype);

解題2

// This is a JavaScript Quiz from BFE.dev

function F() {
this.foo = "bar";
}

const f = new F();
console.log(f.prototype); //undefined,f 是 F 的實例,而不是 F 的原型