JavaScript Object Oriented Programming: Extending Natives
03 Feb 2016Native JavaScript 物件將 method 存在 prototype 中。例如:當一個新的物件被建立,內容為空,但為何可以使用 toString 這個 method?(參考下面的程式碼)
var obj = {};
console.log(obj.toString());
這是因為 var obj = {}
(即 var obj = new Object()
)。而 Object 是 Native JavaScript 的建構式,obj 由 Object 所產生,因此 obj.__proto__ == Object.prototype
。所以,所有在 Object.prototype 中的屬性,obj 皆可使用(包含Object.prototype.toString
)。這就是為什麼 obj 可以使用 toString 這個 method 了。對於 Array、Function 和其他物件也是相同的道理,而它們的 method 即在 Array.prototype、Function.prototype 等。
當物件的屬性被存取時,interpreter 會依照下列的順序找尋屬性的值
- 物件本身
- 物件的
__proto__
- 物件的
__proto__
的__proto__
interpreter 會尋找這個值直到找到或下一個 __proto__
的值為 null。Object.prototype 是唯一一個物件的 __proto__
值為 null,因此一定會終止在 Object.prototype。換句話說,所有的物件皆繼承自 Object。
當基礎型別使用 method 會隱性的轉為物件,而其結果也會是基礎型別。例如:宣告一個變數 i 等於數字 5,並使用 toString 這個 method(參考下面的程式碼)。
var i = 5;
console.log(i.toString()); // 5
修改原生的 Prototypes
Native Prototypes 可以被修改,也可以新增新的 method 到 prototype 中。例如,我們可以新增一個新的 method「each」,將物件的屬性列印出來。
Object.prototype.each = function(f) {
for (var prop in this) {
if (Object.prototype.hasOwnProperty(prop)) continue;
// 跳過後面的指令,進行下一次迴圈的執行。在此用於過濾 protoytpe 的屬性
var value = this[prop];
f.call(value, prop, value);
}
};
var obj = { name: 'John', age: 25 };
obj.each(function(prop, val) {
console.log(prop); // name -> age
});
修改內建的 prototype 是不好的,但如果是要將 ECMA-262 5th 的 method 實作進舊瀏覽器是可以的。例如,假設舊瀏覽器沒有 Object.create
這個 method,那我們就新增它。
if (!Object.create) {
Object.create = function(proto) {
function F() {}
F.prototype = proto;
return new F();
};
}
繼承原生物件
原生物件(Native Objects)是可以被繼承的。例如:Array.prototype
保留了所有的 method 給新的 Array 的實例(instance)。假設我們希望能在 myArray 當中使用 Array.prototype 的 method,那麼就將 Array.prototype 的值設定給myArray.__proto__
即可。
function MyArray() {
// 將陣列中的每個元素用逗點合併,使其成為一字串
this.stringify = function() {
return this.join(', ');
};
}
MyArray.prototype = Array.prototype;
var myArr = new MyArray();
myArr.push('hello');
myArr.push('jack');
console.log(myArr.stringify()); // "hello, jack"
console.log(myArr); // ["hello", "jack"]
console.log(myArr.length); // 2
借用 Method
如果只是想要使用 Array 的部份功能,其實不需要繼承它 - 借用它的 method 即可。
var join = Array.prototype.join,
join = [].join;
var obj = {
0: 'first',
1: 'second',
length: 2,
};
console.log([].join.call(obj, ', ')); // first, second
Array 的 method 常被借用來處理類似 Array 的物件。
Summary
- Native JavaScript 物件將 method 存在 prototype 中。
- Native prototypes 可以被繼承或擴充。
- 修改頂端的 Object.prototype 會破壞 for…in loops,因此不建議這麼做。但可實作一些先進的 method 而讓舊瀏覽器支援這些好用的方法,例如:Object.create、Object.keys、 Function.prototype.bind 等。
References
這篇文章的原始位置在這裡-JavaScript Object Oriented Programming - Extending Natives
由於部落格搬遷至此,因此在這裡放了一份,以便閱讀;部份文章片段也做了些許修改,以期提供更好的內容。