前言

Constructor Function也是Function,那它跟一般的Function差在哪裡,有什麼特別之處,就是今天主要介紹的部分。

構造函式

剛剛有提到其實跟Function差不多,所以正常來說為了要區分這兩者的差別,構造函式在命名上都會以大寫字母來寫,而構造函式的誕生,是要經過一個叫做new這個關鍵字來創立。

意味著,使用new來生成的函式就是構造函式。
沒有使用new來生成,可以直接呼叫執行的,就是普通函式。

那什麼是new

new關鍵字

new這個關鍵字其實是new operator,在 mdn 的解釋是:

The new operator lets developers create an instance of a user-defined object type or of one of the built-in object types that has a constructor function.

我自己翻起來是:new可以創建一個定義物件類型,或者是具有構造函式的內置物件類型,它們的實例。

我的理解是new只可以拿來創造構造函式,沒有用new來創造就只會是普通函式。

接下來就來證明這件事情。

new從原理下手


圖片來源:MDN

假如有個程式如下:

1
2
3
4
5
6
7
8
const Food = function (fruit, color) {
this.fruit = fruit;
this.color = color;
};

const apple = new Food("apple", "red");
console.log(apple);
// Food { fruit: 'apple', color: 'red' }

很明顯,我們後來可以創一個變數,使用new的方式把 apple 跟 red 的資訊,傳遞給上面構造函式,就可以成功創造一個實例。

照著 MDN 上面的解釋,一步步來解析 new 幫忙做了什麼事情:

首先一開始 new 會幫忙創造一個空的物件,可以當作是{}。

然後把 {} 的 __proto__ 指向 Food 的 prototype,這邊就是前幾篇講過的原型概念,這樣的方式是為了要繼承原型鏈。

接著下一步是呼叫這個構造函式,綁定{}是 this 的 context,
可以理解成 this 就會指向這個空物件,構造函式裡面的 this 是什麼,這裡就會是什麼 => this={}。

所以在這個例子來看,現在這個 context 裡面就會有兩個新的屬性。

最後那個一開始創建出來的物件,就會自動回傳,回傳一個有兩個屬性的物件,這就會是new Food("apple", "red")最後結果,它將會儲存在apple這個變數裡面。

以上就是new會在背後幫忙做的事情。

有一個蠻好理解的方式,我從這邊看來的 https://javascript.info/constructor-new。

先看這個程式:

1
2
3
4
5
6
function User(name) {
this.name = name;
this.isAdmin = false;
}

let user = new User("Jack");

這裡的 new User(...) 可以理解成這樣運行:

1
2
3
4
5
6
7
8
9
function User(name) {
// this = {};(隱式創建)

// 添加屬性到 this
this.name = name;
this.isAdmin = false;

// return this;(隱式返回)
}

所以最後返回的結果都是相同的物件:

1
2
3
4
let user = {
name: "Jack",
isAdmin: false,
};

另外,從這裡還可以衍生一件事情,上面有提到說,在創造new時就已經把所有重要的東西都放入 this 裡面,所以不會需要再return回傳結果,所以在這裡可以再做一個劃分:

  • 普通函式會使用return
  • 構造函式不會使用return

什麼情況不能用new

不是 function 的時候:
我個人測試沒有 function 就會拋出異常:

1
2
3
4
5
var Apple = {};
var newApple = new Apple();

console.log(newApple);
//TypeError: Apple is not a constructor

Apple is not a constructor,我明明已經弄成物件了,卻還是不行,那假如用 function:

1
2
3
4
5
var Apple = function () {};
var newApple = new Apple();

console.log(newApple);
//Apple {}

改成用 function 就不會報錯。
我的理解是,空的也不會報錯,基本上全部的函式都可以當作構造函式來使用,除了箭頭函式之外(它沒有 this),除此之外,都可以通過用 new 的方式來變成構造函式。

總結

語法的產生都有其原因的存在,而構造函式的產生是來自於有許多情境會需要創造很多相似的物件。

在創造其他物件時,只需要 new ABC(“abc”),除了更好閱讀之外,也可以少寫許多東西,讓整個程式變得乾淨許多。

這種寫一次就可以反覆使用,就是構造函式的精髓所在,大家也多使用構造函式來優化自己的程式碼吧。

reference

[1] MDN - constructor
[2] Constructor, operator “new”
[3] MDN - new operator
[4] JS 对象机制深剖——new 运算符