在先前的筆記中,我們談到可以使用 function constructor 來建立物件,同時我們也提到 function constructor 這種概念是用來模仿 Java 語言而產生的(參考:[筆記] 談談 JavaScrip t中的 function constructor 和關鍵字 new),在其它的程式語言中,會用 class 這個關鍵字來設定該物件要長什麼樣子,然後透過關鍵字 new 來建立物件。
然而,和其他程式語言不同的地方在於,JavaScript 實際上使用的是 prototypal inheritance 而不是 classic inheritance,所以為了讓 JavaScript 回歸單純的 prototypal inheritance,現在的瀏覽器大部分都支援Object.create() 這種單純的方式來建立物件。
讓我們透過下面的例子,更清楚 pure prototypal inheritance 和 Object.create() 的操作。
首先,我們先使用下面這段程式建立一個原型的物件:
var Person = { firstName: 'Default', lastName: 'Default', getFullName: function () { return this.firstName + " " + this.lastName; } }
注意一下 return 的地方,這裡我們用的是 this.firstName 而不是直接用 firstName。當我使用 this 這個關鍵字的時候,它會指稱到的是 Person 這個物件,但是如果沒有使用 this 的話,會變成在 getFullName 這個 execution context 中找 firstName 和 lastName 這兩個變數,如果找不到的話就會往最外層的全域環境(global environment)去找,最後如果還是找不到,就會出現 "Uncaught ReferenceError: firstName is not defined" 。
Object.create() 的使用
接下來我們說明如何使用 Object.create():
var john = Object.create(Person); console.log(john);
輸出的結果如下,它是一個空物件,但是它繼承了 Person 這個物件當中的屬性和方法:
透過 Object.create() 可以建立一個空物件,同時可以將你帶入 Object.create() 的參數內容變成該物件的原型。
由於原型鍊(prototype chain),可以在這個物件當中建立相同屬性的內容,在執行的時候它會先找物件中最上層的屬性,於是就不會得到 default 的結果,像是這樣:
var Person = { firstName: 'Default', lastName: 'Default', getFullName: function () { return this.firstName + " " + this.lastName; } } var john = Object.create(Person); john.firstName = 'John'; john.lastName = 'Doe'; console.log(john.getFullName());
如此,對於 firstName 和 lastName 來說,在該物件就已經有這兩個屬性,因此它不會在往該物件的原型去尋找,而對 getFullName 來說,因為在 john 這個物件裡沒有這個方法,於是就會到 prototype 裡面去找,最後會回傳 "John Doe"。
透過 Object.create() 這種方法,是最單純使用 prototypal inheritance 的方式。如果你想要定一個物件的原型,就先建立一個 A 物件當做其他物件的基礎,然後再建立另一個空物件 B,指稱 A 物件當做它的原型,在透過為 B 物件賦予屬性或方法。
使用 Object.create() 的方式運用繼承和原型的概念能夠非常簡便的建立物件。
如果瀏覽器不支援 Object.create()
雖然大部分的新瀏覽器都支援這樣的做法,但如果某些瀏覽器的版本真的舊到無法使用 Object.create() 的話,可以怎麼辦呢?
這時候我們會寫一些程式來填補某些瀏覽器不支援的情況,我們把這些程式稱做 polyfill。
如果我們不確定瀏覽器是不是有支援 Object.create() 的話,我們可以寫如下的 polyfill:
// polyfill for Object.create() 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() 的函式,如果沒有的話就會回傳 undefined ,在前面加一個 ! 這個邏輯運算子,則會把 undefined 轉換成布林值,所以這段程式碼轉成中文的話,意思就是「如果 Object.create 不存在的話,則執行... 」。
當 Object.create 不存在的時候,接著會建立 Object.create 這個 function。 if(arguments.length > 1) 是說明如果所代入的參數超過一個的話,會在 console.log 中回傳錯誤訊息。
最後會去執行 Object.create 這個函式原本會執行的內容,也就是先建立一個空的函式 F(){ },然後把原本基礎物件的內容放入 F.prototype 中,最後再用函式建構式(function constructors)的方式,回傳 new F( ) ,如此,就能夠達到 Object.create() 原本的效果。
程式範例
// 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(); }; } var Person = { firstName: 'Default', lastName: 'Default', getFullName: function () { return this.firstName + " " + this.lastName; } } var john = Object.create(Person); john.firstName = 'John'; john.lastName = 'Doe'; console.log(john.getFullName());