我們已經知道透過 new 運算子呼叫函數建構子可以建立物件,
但這種方式是為了模仿其他程式語言(Java、C#)透過 Class 與 new 關鍵字建立物件的方式,
其他程式語言(Java、C#)透過 Class 定義物件,
JavaScript 則是透過 new 運算子呼叫函數建構子的prototype
屬性來定義物件的原型屬性,來達到類似的效果,
在新版本的瀏覽器中已經內建新的建立物件的方法了,
透過 Object.create
方法可以讓我們建立物件,
我們先建立一個物件,
程式碼如下:
var person = {
firstname: 'Default',
lastname: 'Default',
greet: function() {
return 'Hi ' + firstname;
}
};
在透過Object.create
來建立一個新物件,
程式碼如下:
var person = {
firstname: 'Default',
lastname: 'Default',
greet: function() {
return 'Hi ' + firstname;
}
};
var jimmy = Object.create(person);
console.log(jimmy);
在 Console 中的結果如下圖:
會發現 Object.create
方法回傳了一個空物件給我們,
這時點一下空物件左邊的箭頭來察看空物件的原型屬性,
在 Console 中的結果如下圖:
會發現空物件的原型屬性裡有 person 物件的屬性與方法,
這是因為我們把 person 物件當作參數傳入Object.create
方法,
這時候我在新建立的物件取用 greet 方法,
程式碼如下:
var person = {
firstname: 'Default',
lastname: 'Default',
greet: function() {
return 'Hi ' + firstname;
}
};
var jimmy = Object.create(person);
console.log(jimmy.greet());
我能夠正確取用 greet 方法嗎?
在 Console 中的結果如下圖:
這是因為我在 person 物件的 greet 方法中在回傳屬性 firstname 時,沒有加上this,
我們修改一下程式碼,
程式碼如下:
var person = {
firstname: 'Default',
lastname: 'Default',
greet: function() {
return 'Hi ' + this.firstname;
}
};
var jimmy = Object.create(person);
console.log(jimmy.greet());
在 Console 中的結果如下圖:
現在有正確的輸出結果了,這是因為 person 物件的 greet 方法如果在回傳 firstname 屬性時,
沒有透過 this 來取用,這會只在 greet 方法中找尋 firstname 變數,
如果找不到會往全域物件裡面找,
因為在全域物件中我們沒有定義 firstname 變數,所以回傳 undefined
,
person 物件被執行時沒有自己的執行環境,
但如果我在 greet 方法回傳 firstname 時透過 this 來取用 firstname 就可以取用到 person 物件中的 firstname 屬性了,因為物件方法中的 this 指向物件本身,
我也可以用透過Object.create
方法建立的空物件來改變 person 物件的屬性值,
程式碼如下:
var person = {
firstname: 'Default',
lastname: 'Default',
greet: function() {
return 'Hi ' + this.firstname + ' ' + this.lastname;
}
};
var jimmy = Object.create(person);
jimmy.firstname = 'jimmy';
jimmy.lastname = 'Huang';
console.log(jimmy.greet());
透過點(成員取用)運算子再次覆寫屬性就可以讓物件有不同的屬性值,
在 Console 中的結果如下圖:
透過這種方式能夠覆寫、隱藏傳入Object.create
方法的物件的屬性或方法,
透過這種方式添加屬性或物件到 person 物件,
其他我同樣用 Object.create
方法並把 person 物件當作參數傳入的其他新建立的物件也會有新添加的屬性或方法,
這就是純粹的原型繼承,這很直覺,讓你可以直接存取改變原型屬性內的屬性與方法,
在這個例子裡 ,當我們將 person 物件當作Object.create
方法的參數傳入,
person 物件其實就是每個透過Object.create
方法建立的物件的原型屬性,
有些舊版的瀏覽器(IE8),沒有支援Object.create
方法,
這時就需要透過 polyfill 來添加功能到瀏覽器,
polyfill 可以理解成我們在瀏覽器中額外添加的功能(程式碼),
程式碼如下:
// polyfill
if(!Object.create) {
Object.create = function(o) {
if(arguments.length > 1) {
throw new Error('Object.create implementation'
+ ' only accepts the first parameter.');
}
function F() {}
F.prototype = o;
return new F();
}
}
在 if 陳述句的 ()中透過 一元運算子 !,來檢查瀏覽器是否有Object.create
方法可以使用,
由於 Object.create
是個方法,所以將匿名函數指派給Object.create
,
匿名表達式的參數接收一個物件,如果傳入的參數大於一個就報錯,
這是透過執行環境中的 arguments 屬性來得知參數量,
最重要的部份是在這個自建的匿名函數中新增一個空函數 F,
並將傳入的物件指派給空函數 F 的prototype
,
最後透過 new 運算子呼叫函數 F ,這會回一個空物件,
空物件的原型屬性是函數 F 的prototype
,
由於函數 F 的 prototype
屬性被設成傳入的物件,
所以回傳的空物件的原型屬性就是傳入給Object.create
方法當作參數的物件,
這就是 polyfill 想要做的事情,
讓舊版本瀏覽器也可以使用Object.create
所以把物件當作 Object.create
方法的參數傳入,
最後回傳的新物件的原型屬性會是你傳入 Object.create
方法當作參數的物件,
只要修改新物件的屬性或方法,就能覆寫、隱藏及添加屬性或方法給傳入 Object.create
方法當作參數的物件,
傳入 Object.create
方法當作參數的物件也就是新物件的原型屬性,
現在大部分的瀏覽器都已經支援Object.create
方法了,
如果你使用一些新的 JavaScript 語法(ES 7 8)在舊版瀏覽器無法正確執行,
也可以找找看有無那個語法的 polyfill,
像是 MDN 就有提供不少現成的 polyfill,讓我們方便使用,