JavaScript 设计模式——构造器模式

161 阅读3分钟

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 是一个构造函数,它接受 nameage 两个参数,并为每个创建的对象分配这些属性和一个 sayHello 方法。

构造器模式的深入理解

1. this 关键字的作用

在构造函数中,this 关键字指向新创建的对象实例。当我们使用 new 关键字调用构造函数时,JavaScript 引擎会执行以下操作:

  1. 创建一个新的空对象
  2. 将这个空对象的 __proto__ 属性指向构造函数的 prototype 属性
  3. 将这个空对象作为 this 上下文传递给构造函数
  4. 如果构造函数没有返回其他对象,则返回这个新创建的对象

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 中创建对象的基础模式,它允许我们:

  1. 使用构造函数创建具有相同属性和方法的对象实例
  2. 通过原型共享方法,节省内存
  3. 实现继承,创建更复杂的对象层次结构
  4. 在实际开发中封装实体(如用户、产品等)和功能(如 DOM 操作)

虽然现代 JavaScript 提供了更简洁的类语法,但理解构造器模式仍然很重要,因为它帮助我们理解 JavaScript 对象模型的工作原理,并且在处理较老的代码库时仍然会遇到这种模式。