這篇教學將介紹 JavaScript 中兩種建立物件的方法:使用 JavaScript new 及 Function Constructor (建構函式) 以及 ES5 Object.create()

目錄

JavaScript new 和 Function Constructor (建構函式)

在 JavaScript 中,使用 new 是最常見的建立新物件的方法之一。那麼要如何使用 new 呢?

首先我們需要定義一個 function constructor (建構函式)。簡單來說,function constructor 是一個用來創造新物件的函式,通常會和 new 一起搭配使用。

要如何定義建構式呢?舉個例子,如果要建構Cat物件,並且每個物件都有各自的name特性(property),那我們可以定義Cat建構式如下:

// Constructor
function Cat(name) {
	this.name = name;
}

那我們要如何定義物件的方法呢?答案是將方法定義在prototype屬性裡。例如:

// Define 'speak' method for Cat objects
Cat.prototype.speak = function() {
	console.log(this.name + ": meow!");
};

定義完建構式和方法後,我們用new運算子呼叫建構式:

var kitty = new Cat("Kitty");
kitty.speak(); // Kitty: meow!

這樣就創造出了一個kitty物件,可以呼叫我們定義的speak方法!

簡單總結一下,建構式有幾個必要的步驟:

  1. 定義建構式
  2. 將方法定義在建構式的prototype特性裡
  3. new運算子呼叫建構式

new 運算子和 Function Constructor (建構函式) 的運作原理

要了解其運作原理,首先我們要了解何謂 prototypal inheritance (原型繼承)。

所謂的 prototypal inheritance 用一句話形容就是:JavaScript 每個物件都有個 prototype,物件能夠繼承 prototype 上的屬性或方法;如果物件上找不到某個屬性或方法時,就會去查詢它的 prototype 是否存在這個屬性或方法。

這個機制使我們可以在prototype物件上定義特性或方法,所有繼承同一個prototype的物件都可以透過原型委託使用這些特性或方法。

想知道 prototype 更詳細的原理可以看這篇喔。

延伸閱讀:JavaScript Prototype

接著我們來瞭解new運算子的機制。

當我們呼叫new Cat("Kitty")的時候,JS在背後做了幾件事:

  1. 建立新物件,
  2. 新物件的繼承自建構式的prototype特性,也就是Cat.prototype
  3. 將新物件綁定到建構式的this物件,並呼叫建構式。
  4. (在不特別寫明return值的情況下) 回傳剛創造的新物件。

第2步將新物件的原型設為 Cat.prototype。所以對新物件呼叫speak()方法時,會先在物件本身尋找此方法。然後會發現自己身上找不到此方法,於是再到自己的prototype,也就是 Cat.prototype 上尋找。因為我們定義了 Cat.prototype.speak,所以可以順利找到此方法。

簡單地說,當你使用建構式來創造新物件,新物件的原型就是建構式上的prototype特性。而在原型上定義方法,就等於所有物件都可以透過原型委託的方式使用原型上的方法。

使用 new 與 Contructor Function 容易犯的錯誤

建構式必須和new運算子搭配使用,但萬一我們忘了,直接呼叫建構式:

var kitty = Cat("kitty");

此時並不會有任何錯誤或警告,this會直接bind到全域變數,有可能會導致很難察覺的bug!

Object.create()創造新物件

ES5中提供了Object.create()的方法,用來創造新物件。使用方法:

Object.create(proto[, propertiesObject])

這個 function 會回傳一個新物件,其 prototype 等於第一個被傳入的參數。

例如,我們想要創造很多貓物件,所以我們先創造一個物件 cat 來當作 prototype,裡面定義了speak()方法:

var cat = {
	speak: function() {
		console.log(this.name + ": meow!");
	}
};

當我們呼叫Object.create(cat)時,回傳的新物件的 prototype 就是 cat

// Create a new cat
var kitty = Object.create(cat);
kitty.name = "Kitty";
kitty.speak(); // Kitty: meow!

kitty物件裡找不到speak()方法,於是接下來到他的原型物件(也就是cat物件)上面尋找。cat物件裡定義了speak()方法,於是呼叫成功。

Object.create() 的原理

被傳進作為參數的物件,將會被當成新物件的原型物件。所以Object.create()的內部可能會長得像這樣(示意):

if (!Object.create) {
	Object.create = function(o) {
		function F() {}
		F.prototype = o;
		return new F();
	};
}

其中F()是建構式,建構式上的prototype特性設為o,並且由new運算子呼叫建構式。所以新物件的特性查找將會委託給o

使用Object.create()的好處是,省去了可能會忘記用 new 呼叫建構式的風險。

結論

JS中可以用建構式,或者是ES5的 Object.create() 來創造新物件。

使用建構式創造的新物件,將繼承自建構式上的 prototype 屬性。

使用Object.create(obj)創造的新物件,將繼承自obj

參考資料