前言 :
在 建構式(Constructor) 中看過一個例子 :
- function Person(name, age) {
- this.name = name;
- this.age = age;
- this.toString = function() {
- return '[' + this.name + ', ' + this.age + ']';
- };
- }
但函式內容並沒有綁定特定資源. 為了節省記憶體,你也許可以這麼撰寫 :
- function toString() {
- return '[' + this.name + ', ' + this.age + ']';
- }
- function Person(name, age) {
- this.name = name;
- this.age = age;
- this.toString = toString;
- }
使用 new 關鍵字時,JavaScript 會先建立一個空物件,接著設定物件的原型為函式的 prototype 特性所參考的物件,然後呼叫建構式並將所建立的空物件設為 this.
JavaScript 在尋找特性名稱時,會先在實例上找尋有無特性,以上例而言,p1 上會有 name 與 age 特性,所以你可以直接取得對應的值。如果物件上沒有該特性,會到物件的原型上去尋找,以上例而言,p1 上沒有 toString 特 性,所以會到 p1 的原型上尋找,而 p1 的原型物件此時也就是 Person.prototype 所參考的物件,這個物件上有 toString 特性,所以可以 找到 toString 所參考的函式並執行.
要注意的是,只有在查找特性,而物件上不具該特性時才會使用原型,如果你對物件設定某個特性,是直接在物件上設定了特性,而不是對原型設定了特性. 例如 :
在上例中你可以看到,你對 s 所參考的物件設定了 data 特性,但並不影響 Some.prototype.data 的值.
你可以在任何時間點對函式的 prototype 新增特性,由於原型查找的機制,透過函式而建構的所有實例,都可以找到該特性,即使實例建立之後,特性才被添加到原型中. 例如 :
在 建構式(Constructor) 中有提過,每個透過 new 建構的物件,都會有個 constructor 特性,參考至當初建構它的函式. 事實上,每個 Function 實例 建立時,都會在 Function實例 上以空物件建立 prototype,然後在空物件上設定constructor 特性,也因此每個 new 建構的物件,都可以找到 constructor 特性. 例如 :
每 個函式的實例,其 prototype 特性預設參考至 Object 的實例,實例上有個 constructor 特性。根據原型尋找原則,如果 prototype上也找不到,由於 prototype 是 Object 實例,也就是 prototype 的原型預設是參考至 Object.prototype,所以又會到 Object.prototype 上尋找,如果找到就使用,如果沒有找到就是 undefined,這就是 JavaScript 的原型鏈尋找特性機制. 例如 :
上例中 __proto__ 是 Rhino 中一個非標準特性,可以取得物件建立時被設定的原型,預設就是建構式的 prototype 所參考的物件。雖然 Some 實例或 Some.prototype 都沒有定義 xyz,但根據原型鏈查找,最後在 Object.prototype 可以找到 xyz(在Object.prototype上添加特性是非常不建議的,因為它會影響所有 JavaScript 中的物件,這邊只是為了示範原型鏈查找).
你也可以使用 isPrototypeOf() 來確定物件是否為另一物件的原型. 例如 :
在列舉物件特性時,會循著原型鏈一路找出所有可列舉特性. delete 物件的某個特性時,則會循著原型鏈尋找第一個符合的特性並刪除.
Supplement
* W3Schools - JavaScript Objects
沒有留言:
張貼留言