距離上次的你不知道 Combo 有段時間了,這次要端出的是營養更 up up 的滿漢全席原型系列,包括原型、繼承、constructor、class 等,今天先從 constructor 講起
本文可學到:
Constructor 中文譯為「 建構子 」或者是 「 建構式 」,從名稱可以理解應該是要 「建構」也就是創造出什麼,沒錯,在 JS 內可以透過 constructor 製造出結構類似的物件,用雞蛋糕比喻的話,constructor 就是模具,可以依照不同的模具製造出大量相同形狀的雞蛋糕。
從倒入麵糊加熱完成到產生一個雞蛋糕,正式一點的名稱會說,實體化一個物件,產生出來的物件就稱為該建構子的實例 instance。
JavaScript 有內建好的雞蛋糕模具,但也可以設計自己想要的模具,這種模具就稱為 建構式函式 constructor function,加上關鍵字 new
就可以來做雞蛋糕囉!
new
+ constructor function
既然叫 constructor function,模具就是透過 function 做出來的,直接實作一個看看
// 做一個名為 RabbitCake 的模具
function RabbitCake(flavor) {
this.producer = 'Hooo';
this.shape = 'rabbit';
this.flavor = flavor;
}
let rabbit1 = new RabbitCake('cream');
let rabbit2 = new RabbitCake('chocolate');
定義一個名為 RabbitCake 的模具,希望做出來的每個雞蛋糕都印有三個資訊,製作者: Hoo、形狀: rabbit、口味:個別指定。
模具好了就可以來做雞蛋糕囉,依照食譜說的要用 new
+ 模具名稱,參數放想要的口味,第一個 rabbit1 是奶油口味,第二個 rabbit2 是巧克力口味。
印出 rabbit1 和 rabbit2 看看成功了嗎?
console.log(rabbit1) // RabbitCake {producer: "Hooo", shape: "rabbit", flavor: "cream"}
console.log(rabbit2) // RabbitCake {producer: "Hooo", shape: "rabbit", flavor: "chocolate"}
成功! 兩個雞蛋糕內都有我們需要的資訊
input 到 output 中間的過程發生了什麼事,繼續往下深究吧
仔細看程式碼,發現其實 RabbitCake 就是個普通的 function,沒有回傳值出去,照理來說賦值到變數上應該印出 undefined
呀
let rabbit3 = RabbitCake('cream')
console.log(rabbit3) // undefined
但上面印出的 rabbit1 是個含有三個屬性的物件,中間花生什麼事?
其實是關鍵字 new
發揮作用啦
new
時, 會先有一個空物件被建立在 RabbitCake 內加上 console.log(this)
來看看新物件屬性一個個創建的過程
function RabbitCake(flavor) {
console.log(this) // 一開始被創建的空物件
this.producer = 'Hooo';
console.log(this) // 加入第一個屬性 producer
this.shape = 'rabbit';
console.log(this) // 加入第二個屬性 shape
this.flavor = flavor;
console.log(this) // 加上最後一個屬性 flavor,值來自參數
}
let rabbit1 = new RabbitCake('cream');
整理一下使用建構式函式的重點
new
關鍵字來創造物件,因此給了特別的名稱function RabbitCake(flavor) {
this.producer = 'Hooo';
this.shape = 'rabbit';
this.flavor = flavor;
return {};
}
let rabbit1 = new RabbitCake('cream');
console.log(rabbit1) // {}
前面提到 JavaScript 有做好的雞蛋糕模具,他們在哪呢?存在全域物件下
打開瀏覽器的開發者工具,輸入 window 可以看到全域物件的所有屬性,根據 ECMA 19.3 中列出來的 constructor 有 40 種!
可以發現這些 constructor 都是 function 以大寫開頭,昨天學到的 Math 是物件所以不是 constructor 唷!
各大型別也算在內,加上 new
關鍵字後可以創造物件,而本身是 function 也可以呼叫
// 作為 function 呼叫
String() // string
Number() // number
Boolean() // boolean
Date() // string
Array() // object
Object() // object
Function() // function
// 加上new 作為constructor呼叫
new String() // object
new Number() // object
new Boolean // object
new Date() // object
new Array() // object
new Object() // object
new Function() // function
其實不建議對基本型別 constructor 使用 new
關鍵字,因為創出來的都是物件型別,如果能用字面值 literal 表達,就無需動用到 constructor 啦!
在查找 ECMA 時,對於其中描述 Array()、Function() constructor 這兩段很困惑
also creates and initializes a new Array when called as a function rather than as a constructor. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.
creates and initializes a new function object when called as a function rather than as a constructor. Thus the function call Function(…) is equivalent to the object creation expression new Function(…) with the same arguments. - 20.2.1 The Function Constructor
原文意思是當 Array
作為一個 function 而不是 constructor 呼叫時,會建立並初始化一個新的陣列,因此當帶入的參數相同時,使用 Array()
呼叫和作為 constructor呼叫 new Array()
是一樣結果。 (Function 翻譯亦同)
來實驗看看
// 呼叫 Array as a function
let arr = Array(3).fill('Hi')
console.log(arr) // ['Hi', 'Hi', 'Hi']
// 呼叫 Array as a constructor
let brr = new Array(3).fill('Hi')
console.log(brr) // ['Hi', 'Hi', 'Hi']
// 呼叫 Function as a function
let afun = Function('a', 'b', 'return a+b')
console.log(afun(2,3)) // 5;
console.log(afun) // function(a, b) { return a+b}
// 呼叫 Function as a constructor
let bfun = new Function('a', 'b', 'return a+b')
console.log(bfun(2,3)) // 5
console.log(bfun) // function(a, b) { return a+b}
真的欸,使用 Array()、Function() 都是建立出一個新的陣列或函式,與加上 new關鍵字產生的結果是相同的!
瞭解建構子如何創建物件後,下一章來講講 this 到底是誰
ECMA
W3Schools
Constructor, operator "new"
建構物件範本:Constructor Function
[筆記] 談談 JavaScript 中內建的 function constructors 及應注意的地方
建構式函式若 return 物件,便覆蓋掉上述定義的 this 屬性,因此定義建構式函式時不使用 return
這個很棒~~常常會忘記這個特性
雞蛋糕模具的比喻讚!