JavaScript 設計模式與開發實踐 - Chapter 1

April 13, 2016

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);

上面這段程式碼的執行方式是這樣的: 1. p 先去找它所有的屬性中有沒有 name 這個屬性,但是並沒有 2. 接著尋找 name 屬性這個請求委託給了物件 P 的原型建構子,被 p.__proto__ 所記錄並指向到 P.prototype 3. 在 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);

執行的方式: 1. b 屬性中沒有 name 這個屬性,所以將這個委託給 b 的建構子原型,透過 b.__proto__ 來指向 B.prototype。 2. B.prototype 設定成一個 new A 的物件。 3. 在 new A 中依然沒這個屬性,再委託給 A.prototype。 4. 在 A.prototype 找到 name 並回傳。

假設連 A.prototype 也沒有的話呢?那就會再往 A 的建構子原型繼續傳遞,也就是 Object.prototype