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 引擎在背後做了幾件事:
- 建立新物件。
- 將新物件的 prototype 指定為建構函式的
prototype
屬性。以上面的例子來說就是Cat.prototype
。 - 將新物件綁定到
this
物件,並呼叫建構函式。 - 在不特別指定
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()
來創造新物件。
定義建構函式的必要步驟:
- 宣告建構函式。
- 在建構函式中,將物件屬性定義在
this
上。 - 將物件方法定義在建構式的
prototype
屬性裡。 - 用
new
運算子呼叫建構函式。
使用 Object.create(obj)
方法創造的新物件,將繼承自 obj
。