JavaScript 设计模式——构造器模式详解
什么是构造器模式?
构造器模式(Constructor Pattern)是 JavaScript 中最基础也是最重要的设计模式之一。它允许我们使用构造函数来创建特定类型的对象,这些对象具有相同的属性和方法。
在 JavaScript 中,构造函数是用来创建对象的特殊函数。当我们使用 new 关键字调用一个函数时,这个函数就变成了构造函数。
基础示例
让我们从一个简单的例子开始:
// 定义一个构造函数
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
}
// 使用构造函数创建对象
const person1 = new Person('Alice', 25);
const person2 = new Person('Bob', 30);
person1.sayHello(); // Hello, my name is Alice and I am 25 years old.
person2.sayHello(); // Hello, my name is Bob and I am 30 years old.
在这个例子中,Person 是一个构造函数,它接受 name 和 age 两个参数,并为每个创建的对象分配这些属性和一个 sayHello 方法。
构造器模式的深入理解
1. this 关键字的作用
在构造函数中,this 关键字指向新创建的对象实例。当我们使用 new 关键字调用构造函数时,JavaScript 引擎会执行以下操作:
- 创建一个新的空对象
- 将这个空对象的
__proto__属性指向构造函数的prototype属性 - 将这个空对象作为
this上下文传递给构造函数 - 如果构造函数没有返回其他对象,则返回这个新创建的对象
2. 使用原型优化构造器模式
在上面的基础示例中,每次创建新的 Person 实例时,都会创建一个新的 sayHello 函数。这会导致内存浪费。我们可以使用原型来解决这个问题:
// 定义构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在原型上定义方法
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
Person.prototype.getAgeNextYear = function() {
return this.age + 1;
};
// 创建实例
const person1 = new Person('Alice', 25);
const person2 = new Person('Bob', 30);
person1.sayHello(); // Hello, my name is Alice and I am 25 years old.
person2.sayHello(); // Hello, my name is Bob and I am 30 years old.
console.log(person1.getAgeNextYear()); // 26
console.log(person2.getAgeNextYear()); // 31
// 验证方法是否共享
console.log(person1.sayHello === person2.sayHello); // true
通过将方法定义在原型上,所有实例都会共享同一个方法,从而节省内存。
更复杂的示例
让我们看一个更复杂的例子,展示构造器模式在实际开发中的应用:
// 定义一个基础的 Vehicle 构造函数
function Vehicle(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
// 在原型上定义通用方法
Vehicle.prototype.getAge = function() {
const currentYear = new Date().getFullYear();
return currentYear - this.year;
};
Vehicle.prototype.getInfo = function() {
return `${this.year} ${this.make} ${this.model}`;
};
// 定义一个 Car 构造函数,继承自 Vehicle
function Car(make, model, year, doors) {
// 调用父构造函数
Vehicle.call(this, make, model, year);
this.doors = doors;
}
// 设置原型链
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;
// 为 Car 添加特定方法
Car.prototype.honk = function() {
console.log('Beep beep!');
};
// 定义一个 Motorcycle 构造函数,也继承自 Vehicle
function Motorcycle(make, model, year, engineSize) {
// 调用父构造函数
Vehicle.call(this, make, model, year);
this.engineSize = engineSize; // 发动机排量
}
// 设置原型链
Motorcycle.prototype = Object.create(Vehicle.prototype);
Motorcycle.prototype.constructor = Motorcycle;
// 为 Motorcycle 添加特定方法
Motorcycle.prototype.wheelie = function() {
console.log('Popping a wheelie!');
};
// 创建实例
const myCar = new Car('Toyota', 'Camry', 2020, 4);
const myMotorcycle = new Motorcycle('Harley-Davidson', 'Street 750', 2019, '750cc');
console.log(myCar.getInfo()); // 2020 Toyota Camry
console.log(myCar.getAge()); // 根据当前年份计算
myCar.honk(); // Beep beep!
console.log(myMotorcycle.getInfo()); // 2019 Harley-Davidson Street 750
console.log(myMotorcycle.getAge()); // 根据当前年份计算
myMotorcycle.wheelie(); // Popping a wheelie!
实际开发中的应用
1. 用户管理系统
在实际的 Web 应用中,构造器模式经常用于创建用户对象:
// 用户构造函数
function User(id, username, email, role = 'user') {
this.id = id;
this.username = username;
this.email = email;
this.role = role;
this.createdAt = new Date();
this.isActive = true;
}
// 原型方法
User.prototype.activate = function() {
this.isActive = true;
console.log(`${this.username} is now active.`);
};
User.prototype.deactivate = function() {
this.isActive = false;
console.log(`${this.username} is now deactivated.`);
};
User.prototype.updateEmail = function(newEmail) {
const oldEmail = this.email;
this.email = newEmail;
console.log(`Email updated from ${oldEmail} to ${newEmail}`);
};
User.prototype.getInfo = function() {
return {
id: this.id,
username: this.username,
email: this.email,
role: this.role,
isActive: this.isActive,
memberSince: this.createdAt.toDateString()
};
};
// 创建用户实例
const user1 = new User(1, 'alice', 'alice@example.com');
const user2 = new User(2, 'bob', 'bob@example.com', 'admin');
console.log(user1.getInfo());
user1.deactivate();
user1.updateEmail('alice.new@example.com');
console.log(user2.getInfo());
user2.activate();
2. DOM 元素封装
在前端开发中,我们也可以使用构造器模式来封装 DOM 元素的操作:
// DOM 元素封装构造函数
function DOMElement(selector) {
this.element = document.querySelector(selector);
if (!this.element) {
throw new Error(`Element with selector "${selector}" not found`);
}
}
// 原型方法
DOMElement.prototype.setText = function(text) {
this.element.textContent = text;
return this; // 支持链式调用
};
DOMElement.prototype.setHTML = function(html) {
this.element.innerHTML = html;
return this;
};
DOMElement.prototype.addClass = function(className) {
this.element.classList.add(className);
return this;
};
DOMElement.prototype.removeClass = function(className) {
this.element.classList.remove(className);
return this;
};
DOMElement.prototype.toggleClass = function(className) {
this.element.classList.toggle(className);
return this;
};
DOMElement.prototype.on = function(event, handler) {
this.element.addEventListener(event, handler);
return this;
};
// 使用示例
// 注意:以下代码需要在浏览器环境中运行
/*
const button = new DOMElement('button');
button.setText('Click me')
.addClass('btn')
.addClass('btn-primary')
.on('click', function() {
alert('Button clicked!');
});
*/
ES6 类语法
虽然构造器模式在 ES5 中很常见,但在现代 JavaScript(ES6+)中,我们可以使用类语法来实现相同的功能,这通常更清晰易读:
// 使用 ES6 类语法重写上面的 User 示例
class User {
constructor(id, username, email, role = 'user') {
this.id = id;
this.username = username;
this.email = email;
this.role = role;
this.createdAt = new Date();
this.isActive = true;
}
activate() {
this.isActive = true;
console.log(`${this.username} is now active.`);
}
deactivate() {
this.isActive = false;
console.log(`${this.username} is now deactivated.`);
}
updateEmail(newEmail) {
const oldEmail = this.email;
this.email = newEmail;
console.log(`Email updated from ${oldEmail} to ${newEmail}`);
}
getInfo() {
return {
id: this.id,
username: this.username,
email: this.email,
role: this.role,
isActive: this.isActive,
memberSince: this.createdAt.toDateString()
};
}
}
// 使用方式完全相同
const user = new User(1, 'alice', 'alice@example.com');
console.log(user.getInfo());
总结
构造器模式是 JavaScript 中创建对象的基础模式,它允许我们:
- 使用构造函数创建具有相同属性和方法的对象实例
- 通过原型共享方法,节省内存
- 实现继承,创建更复杂的对象层次结构
- 在实际开发中封装实体(如用户、产品等)和功能(如 DOM 操作)
虽然现代 JavaScript 提供了更简洁的类语法,但理解构造器模式仍然很重要,因为它帮助我们理解 JavaScript 对象模型的工作原理,并且在处理较老的代码库时仍然会遇到这种模式。