常規的 {...}
語法允許我們建立一個物件。但我們經常需要建立許多類似的物件,例如多個使用者或選單項目等。
這可以使用建構函式和 "new"
運算子來完成。
建構函式
建構函式在技術上是常規函式。不過有兩個慣例
- 它們以大寫字母開頭命名。
- 它們應僅使用
"new"
運算子執行。
例如
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
alert(user.name); // Jack
alert(user.isAdmin); // false
當函數以 new
執行時,會執行下列步驟
- 建立一個新的空物件並指定給
this
。 - 函數主體執行。通常會修改
this
,新增新的屬性。 - 傳回
this
的值。
換句話說,new User(...)
執行類似下列動作
function User(name) {
// this = {}; (implicitly)
// add properties to this
this.name = name;
this.isAdmin = false;
// return this; (implicitly)
}
因此 let user = new User("Jack")
與下列結果相同
let user = {
name: "Jack",
isAdmin: false
};
現在如果我們要建立其他使用者,可以呼叫 new User("Ann")
、new User("Alice")
等。比每次使用文字簡潔許多,也容易閱讀。
這就是建構函式的用意:實作可重複使用的物件建立程式碼。
再次說明:技術上來說,任何函數(除了箭頭函數,因為它們沒有 this
)都可以當作建構函式使用。它可以用 new
執行,並執行上述演算法。首字母大寫是一種慣例,用來清楚說明函數會以 new
執行。
如果我們有許多程式碼行都與建立單一複雜物件有關,可以使用立即呼叫的建構函式將它們包起來,如下所示
// create a function and immediately call it with new
let user = new function() {
this.name = "John";
this.isAdmin = false;
// ...other code for user creation
// maybe complex logic and statements
// local variables etc
};
這個建構函式無法再次呼叫,因為它沒有儲存在任何地方,只會建立並呼叫。因此,這個技巧的目的是封裝建立單一物件的程式碼,而不會重複使用。
建構函式模式測試:new.target
本節的語法很少使用,除非你想要了解所有內容,否則可以略過。
在函數內,我們可以使用特殊屬性 new.target
來檢查函數是否以 new
呼叫。
對於一般呼叫,它是不定義的;如果以 new
呼叫,它會等於函數
function User() {
alert(new.target);
}
// without "new":
User(); // undefined
// with "new":
new User(); // function User { ... }
可以在函數內使用它來判斷函數是否以 new
(「建構函式模式」)或一般方式呼叫(「一般模式」)。
我們也可以讓 new
和一般呼叫執行相同的動作,如下所示
function User(name) {
if (!new.target) { // if you run me without new
return new User(name); // ...I will add new for you
}
this.name = name;
}
let john = User("John"); // redirects call to new User
alert(john.name); // John
這種方法有時會用在函式庫中,以讓語法更靈活。因此,人們可以選擇使用或不使用 new
呼叫函數,函數仍然會運作。
不過,這可能不是一個到處都適用的好方法,因為省略 new
會讓正在執行的動作變得不太明顯。使用 new
時,我們都知道正在建立新的物件。
建構函式的傳回值
通常,建構函式沒有 return
陳述式。它們的工作是將所有必要的資訊寫入 this
,而它會自動成為結果。
但如果有 return
陳述式,那麼規則很簡單
- 如果以物件呼叫
return
,則會回傳物件,而不是this
。 - 如果以基本型別呼叫
return
,則會略過。
換句話說,以物件呼叫 return
會回傳該物件,在其他所有情況下,則會回傳 this
。
例如,這裡的 return
會透過回傳物件來覆寫 this
function BigUser() {
this.name = "John";
return { name: "Godzilla" }; // <-- returns this object
}
alert( new BigUser().name ); // Godzilla, got that object
以下是一個有空 return
的範例(或我們可以在其後放置一個基本型別,沒關係)
function SmallUser() {
this.name = "John";
return; // <-- returns this
}
alert( new SmallUser().name ); // John
建構函式通常沒有 return
陳述式。我們在此提到回傳物件的特殊行為,主要是為了完整性。
順帶一提,我們可以在 new
之後省略括號
let user = new User; // <-- no parentheses
// same as
let user = new User();
在此省略括號不算是「良好風格」,但規範允許這種語法。
建構函式中的方法
使用建構函式函式來建立物件能提供很大的彈性。建構函式函式可能有參數,用來定義如何建構物件,以及在其中放入什麼。
當然,我們不僅可以將屬性新增到 this
,也可以新增方法。
例如,以下的 new User(name)
會建立一個具有給定 name
和 sayHi
方法的物件
function User(name) {
this.name = name;
this.sayHi = function() {
alert( "My name is: " + this.name );
};
}
let john = new User("John");
john.sayHi(); // My name is: John
/*
john = {
name: "John",
sayHi: function() { ... }
}
*/
若要建立複雜物件,有一個更進階的語法,類別,我們稍後會介紹。
摘要
- 建構函式函式,或簡稱建構函式,是常規函式,但有一個共識,就是將它們命名為開頭大寫字母。
- 建構函式函式只能使用
new
呼叫。此類呼叫表示在開始時建立空的this
,並在結束時回傳已填入資料的this
。
我們可以使用建構函式函式來建立多個類似的物件。
JavaScript 為許多內建語言物件提供建構函式函式:例如用於日期的 Date
、用於集合的 Set
,以及我們計畫研究的其他函式。
留言
<code>
標籤;若要插入多行程式碼,請將它們包覆在<pre>
標籤中;若要插入超過 10 行的程式碼,請使用沙盒(plnkr、jsbin、codepen…)