JavaScript 設計模式與開發實踐 - Chapter 1
Chapter 1
- 所有的資料都是物件。
- 要得到一個物件,不是通過實例化類別,而是找到一個物件的原型並且複製它。
- 物件會記住它的原型。
- 如果物件無法回應某個請求,它會把這個請求委託給它自己的原型。
所有的資料都是物件
JavaScript 中的根物件是 Object.prototype
物件,它是一個空物件,在 JavaScript 中我們遇到的每個物件,都是從 Object.prototype
複製而來的,所以 Object.prototype
是它們的原型。
const obj = {};console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
要得到一個物件,不是通過實例化類別,而是找到一個物件的原型並且複製它
Person 並不是一個類別,而是一個 function 建構子,在 JavaScript 中 function 可以做為普通 function 被呼叫,也可以做為建構子被呼叫。
當使用 new
來呼叫 function 時,此時的 function 就是一個建構子,透過 new
來建立物件的過程,實際上也是先複製 Object.prototype
物件。
function Person(name) {this.name = name;}Person.prototype.getName = function() {return this.name;};const p = new Person('PJ');console.log(p.name);console.log(p.getName());console.log(Object.getPrototypeOf(p) === Person.prototype) // true
物件會記住它的原型
以 JavaScript 真正的實作來說,並不能說物件有原型,而只能說物件的建構子有原型。
物件把請求委託給自己的原型,真正的涵義是物件把請求委託給它的建構子原型。
在 JavaScript 中有一個 __proto__
的隱藏屬性,某個物件的 __proto__
會指向它的建構子的原型物件。
關於這個部分,可以參考 stackoverflow proto VS. prototype in JavaScript 此篇,透過圖片可以更清楚理解。
__proto__
就是物件跟物件建構子的原型聯繫起來的地方。
const a = {};console.log(a.__proto__ === Object.prototype); // ture//obj.__proto__ = Constructor.prototype; // 指向正確的原型,而不是指向 Object.prototype。
如果物件無法回應某個請求,它會把這個請求委託給它自己的原型
const obj = {name: 'pj',};const P = function() {};P.prototype = obj;const p = new P();console.log(p.name);
上面這段程式碼的執行方式是這樣的:
p
先去找它所有的屬性中有沒有name
這個屬性,但是並沒有- 接著尋找
name
屬性這個請求委託給了物件P
的原型建構子,被p.__proto__
所記錄並指向到P.prototype
- 在
P.prototype
設定了一個物件obj
,從裡面找到了name
,並回傳它的值。
另一個範例:
const A = function() {};A.prototype = { name: 'PJ' };const B = function () {};B.prototype = new A();const b = new B();console.log(b.name);
執行的方式:
- b 屬性中沒有
name
這個屬性,所以將這個委託給 b 的建構子原型,透過b.__proto__
來指向B.prototype
。 - B.prototype 設定成一個
new A
的物件。 - 在
new A
中依然沒這個屬性,再委託給A.prototype
。 - 在
A.prototype
找到name
並回傳。
假設連 A.prototype 也沒有的話呢?那就會再往 A 的建構子原型繼續傳遞,也就是 Object.prototype
。