建構函數是 JavaScript 中用來創建和初始化物件的一種特殊函數。
它像一個「模具」,透過 new
關鍵字生成多個相似的物件實例。
new
呼叫時,自動創建新物件並設定 this
。new
會在記憶體的堆中分配空間給新物件,每次創建的實例都獨立,佔用不同的記憶體位置。 function Person(name, age) {
this.name = name; // 屬性
this.age = age;
this.sayHi = function() { // 方法
console.log(`Hi, I'm ${this.name}`);
};
}
new
創建實例const person1 = new Person("Alice", 25);
const person2 = new Person("Bob", 30);
console.log(person1.name); // "Alice"
person1.sayHi(); // "Hi, I'm Alice"
console.log(person2.age); // 30
new 做了什麼?
{}
,並在記憶體中分配獨立空間。this
綁定到這個新物件。實例獨立性:
每個用 new
創建的實例(例如 person1
和 person2
)是獨立的,佔用不同的記憶體位置,互不影響。可以用 person1 === person2
驗證,結果會為 false
)。
function Car(model) {
this.model = model;
this.drive = function() {
console.log(`Driving a ${this.model}`);
};
}
const myCar = new Car("Toyota");
myCar.drive(); // "Driving a Toyota"
步驟:
new Car("Toyota")
:{}
,分配獨立記憶體空間。this
指向這個物件。this.model = "Toyota"
this.drive = function...
myCar
得到 { model: "Toyota", drive: function }
,獨立於其他實例。class
的關係ES6 引入 class,其實是建構函數的語法糖。
// 建構函數
function Person(name) {
this.name = name;
}
Person.prototype.sayHi = function() {
console.log(`Hi, I'm ${this.name}`);
};
// Class 寫法
class PersonClass {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`Hi, I'm ${this.name}`);
}
}
const p1 = new Person("Alice");
const p2 = new PersonClass("Bob");
p1.sayHi(); // "Hi, I'm Alice"
p2.sayHi(); // "Hi, I'm Bob"
差異:
class
是更高階的抽象,內部仍基於原型。class
更直觀,方法自動綁到原型。new
如果直接呼叫 Person("Alice")
(不加 new
),this
會指向全域物件(瀏覽器中是 window
),造成錯誤。
function Person(name) {
this.name = name;
}
Person("Alice"); // 無 new
console.log(window.name); // "Alice"(全域污染)
解法:嚴格模式("use strict"
)會報錯,或檢查 this
:
//嚴格模式
"use strict";
function Person(name) {
this.name = name;
}
Person("Alice"); // 報錯:Cannot set property 'name' of undefined
//檢查 this
function Person(name) {
if (!(this instanceof Person)) {
return new Person(name);
}
this.name = name;
}
const p = Person("Alice"); // 即使忘了 new 也沒問題
console.log(p.name); // "Alice"
正常情況下,new
會返回建構函數創建的物件(this
)。但如果建構函數明確返回一個物件(而不是 undefined
或基本類型),new
會返回這個手動指定的物件,忽略 this。
function Test() {
this.name = "Test";
return { name: "Override" }; // 手動返回物件
}
const t = new Test();
console.log(t.name); // "Override"(而非 "Test")
//對比:
function Test2() {
this.name = "Test";
return "string"; // 返回基本類型
}
const t2 = new Test2();
console.log(t2.name); // "Test"(基本類型被忽略,new 使用 this)