JavaScript new 運算子及建構函式的用法

April 23, 2016

分類標籤:javascript frontend interview

JavaScript 中可以使用 JavaScript new 運算子及建構函式建立物件。定義建構函式的必要步驟:1. 宣告建構函式 2. 在建構函式中,將物件屬性定義在 this 上 3. 將物件方法定義在建構式的 prototype 屬性裡 4. 用 new 運算子呼叫建構函式。JavaScript 中也可以使用 Object.create() 方法建立物件。

目錄

JavaScript new 運算子和建構函式 (Function Constructor)

在 JavaScript 中可以使用 new 運算子搭配建構函式 (function constructor) 建立新物件。

建構函式 (function constructor) 是一個用來創造新物件的函式,需要和 new 運算子一起搭配使用。

舉例來說,假設現在要用 Cat() 建構函式創造新物件,我們需要搭配 new 運算子一起呼叫建構函式 Cat(),呼叫 Cat() 時可以傳入參數:

var kitty = new Cat("Kitty");

在 this 上定義物件屬性

如果要定義物件的屬性,需要在建構函式中修改 this 的屬性 (property)。在建構函式中,this 代表我們要創造的新物件。

舉例來說,假設物件需要有 name 屬性,我們需要將建構函式的 name 參數指定給 this.name。我們可以定義 Cat 建構式如下:

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

搭配 new 運算子呼叫建構函式時,將 "Kitty" 當作建構函式的參數傳入,則新物件的 name 屬性即為 "Kitty"。我們可以呼叫 kitty.name 來驗證:

var kitty = new Cat("Kitty");
console.log(kitty.name) // Kitty

在 prototype 上定義物件方法

要定義物件方法,需要將物件方法宣告在建構函式的 prototype 屬性裡。建構函式的 prototype 屬性,就是新物件的 prototype。

舉例來說,如果 Cat 建構函式產生的物件都要有 speak() 方法,我們可以定義 Cat.prototype.speak()

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

接著用 new 運算子呼叫建構函式創造新物件,就可以在新物件上呼叫 speak() 方法:

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

原型繼承 (Prototypal Inheritance)

要了解 new 和建構函式的運作原理,首先我們要了解何謂原型繼承 (prototypal inheritance)。

原型繼承的意思是,JavaScript 中每個物件都有個 prototype 屬性,物件能夠繼承 prototype 上的屬性或方法。這個機制可以讓我們產生繼承自同一個 prototype 的多個物件,達到代碼復用的效果。

想知道 prototype 更詳細的原理可以看這篇JavaScript Prototype (原型) 是什麼?

用 new 運算子呼叫建構函式時,背後發生了什麼事?

當我們用 new 運算子呼叫建構函式 new Cat("Kitty") 的時候,JavaScript 引擎在背後做了幾件事:

  1. 建立新物件。
  2. 將新物件的 prototype 指定為建構函式的 prototype 屬性。以上面的例子來說就是 Cat.prototype
  3. 將新物件綁定到 this 物件,並呼叫建構函式。
  4. 在不特別指定 return 值的情況下,回傳剛創造的新物件。

因為新物件的原型是 Cat.prototype,所以新物件可以呼叫定義在 Cat.prototype 上的 speak() 方法。

使用 new 運算子與建構函式容易犯的錯誤

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

var kitty = Cat("kitty");

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

用 Object.create() 創造新物件

ES5 中提供了 Object.create() 的方法,用途是創造新物件,並令其 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() 方法,但它的 prototype (也就是 cat 物件) 定義了 speak() 方法,於是可以成功呼叫。

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

Object.create() 的 polyfill

被傳進作為參數的物件,將會被當成新物件的原型物件。所以 Object.create() 的 polyfill 可以這樣寫:

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

其中 F() 是建構函式,將建構函式的 prototype 設為傳入的物件 o,並且由 new 運算子呼叫建構函式,產生新物件。

參考:Object.create() polyfill - MDN

結論

JavaScript 中可以用建構函式搭配 new 運算子,或是 ES5 的 Object.create() 來創造新物件。

定義建構函式的必要步驟:

  1. 宣告建構函式。
  2. 在建構函式中,將物件屬性定義在 this 上。
  3. 將物件方法定義在建構式的 prototype 屬性裡。
  4. new 運算子呼叫建構函式。

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

參考資料


Profile picture

Shubo Chao 軟體工程師,目前大多專注於前端開發