返回

带你秒懂JavaScript常见面试题:掌握组合式继承与发布订阅模式,征服面试官!

前端

**** JavaScript 继承、设计模式:面试锦囊**

组合式继承

组合式继承是一种通过将子类构造函数与父类构造函数相结合来实现继承的 JavaScript 方法。它继承了父类的属性和方法,同时也允许子类定义自己的属性和方法。步骤如下:

  1. 定义父类构造函数: 创建父类构造函数,其中包含父类属性和方法。
  2. 定义子类构造函数: 创建子类构造函数,其中包含子类属性和方法。
  3. 在子类构造函数中调用父类构造函数: 在子类构造函数中,调用父类构造函数来继承父类的属性和方法。
// 父类构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 父类方法
Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// 子类构造函数
function Student(name, age, major) {
  // 调用父类构造函数
  Person.call(this, name, age);
  this.major = major;
}

// 子类方法
Student.prototype.study = function() {
  console.log(`I am studying ${this.major}.`);
};

寄生式继承

寄生式继承通过创建一个新的对象来继承父类的属性和方法,然后将这个新对象作为子类的原型对象来实现继承。它继承了父类的属性和方法,但不污染子类构造函数。步骤如下:

  1. 定义父类构造函数: 创建父类构造函数,其中包含父类属性和方法。
  2. 创建新对象: 创建一个新的对象,并为其添加父类构造函数的属性和方法。
  3. 将新对象作为子类的原型对象: 将新对象作为子类的原型对象,以使其继承父类的属性和方法。
// 父类构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// 父类方法
Person.prototype.greet = function() {
  console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};

// 创建新对象
const personPrototype = {
  greet: Person.prototype.greet
};

// 子类构造函数
function Student(name, age, major) {
  this.name = name;
  this.age = age;
  this.major = major;
}

// 将新对象作为子类的原型对象
Student.prototype = personPrototype;

发布订阅模式

发布订阅模式是一种设计模式,它允许对象之间进行通信,而无需它们直接相互引用。它用于解耦对象之间的关系,使其更容易维护和扩展。步骤如下:

  1. 定义发布者对象: 创建发布者对象,其中包含发布事件的方法。
  2. 定义订阅者对象: 创建订阅者对象,其中包含订阅事件的方法。
  3. 发布者对象调用发布事件的方法: 发布者对象调用发布事件的方法,并将事件数据传递给订阅者对象。
  4. 订阅者对象收到事件数据后: 订阅者对象收到事件数据后,执行相应的处理逻辑。
// 发布者对象
const publisher = {
  subscribers: [],
  
  // 订阅事件
  subscribe: function(subscriber) {
    this.subscribers.push(subscriber);
  },
  
  // 取消订阅事件
  unsubscribe: function(subscriber) {
    this.subscribers = this.subscribers.filter(s => s !== subscriber);
  },
  
  // 发布事件
  publish: function(data) {
    this.subscribers.forEach(subscriber => subscriber(data));
  }
};

// 订阅者对象
const subscriber1 = function(data) {
  console.log(`Subscriber 1 received data: ${data}`);
};

const subscriber2 = function(data) {
  console.log(`Subscriber 2 received data: ${data}`);
};

// 订阅事件
publisher.subscribe(subscriber1);
publisher.subscribe(subscriber2);

// 发布事件
publisher.publish('Hello, world!');

观察者模式

观察者模式是发布订阅模式的一种特殊情况,它允许对象之间进行通信,但订阅者对象不能直接访问发布者对象。步骤如下:

  1. 定义被观察者对象: 创建被观察者对象,其中包含发布事件的方法。
  2. 定义观察者对象: 创建观察者对象,其中包含订阅事件的方法。
  3. 被观察者对象调用发布事件的方法: 被观察者对象调用发布事件的方法,并将事件数据传递给观察者对象。
  4. 观察者对象收到事件数据后: 观察者对象收到事件数据后,执行相应的处理逻辑。
// 被观察者对象
const observable = {
  observers: [],
  
  // 订阅事件
  subscribe: function(observer) {
    this.observers.push(observer);
  },
  
  // 取消订阅事件
  unsubscribe: function(observer) {
    this.observers = this.observers.filter(o => o !== observer);
  },
  
  // 发布事件
  notify: function(data) {
    this.observers.forEach(observer => observer(data));
  }
};

// 观察者对象
const observer1 = function(data) {
  console.log(`Observer 1 received data: ${data}`);
};

const observer2 = function(data) {
  console.log(`Observer 2 received data: ${data}`);
};

// 订阅事件
observable.subscribe(observer1);
observable.subscribe(observer2);

// 发布事件
observable.notify('Hello, world!');

常见问题解答

  1. 如何选择合适的继承方法?
    • 组合式继承适用于需要子类构造函数中父类属性和方法的情况。
    • 寄生式继承适用于需要继承父类属性和方法,但不想污染子类构造函数的情况。
  2. 发布订阅模式和观察者模式有何区别?
    • 发布订阅模式中,订阅者对象可以直接访问发布者对象。
    • 观察者模式中,订阅者对象不能直接访问发布者对象。
  3. 何时使用发布订阅模式?
    • 解耦对象之间的关系,使其更容易维护和扩展。
    • 允许对象异步通信。
  4. 何时使用观察者模式?
    • 当被观察者对象和观察者对象需要松散耦合时。
    • 当需要通知多个观察者对象时。
  5. 设计模式的优点是什么?
    • 重用性:提高代码的可重用性。
    • 可扩展性:简化代码的扩展和维护。
    • 解耦性:降低不同模块之间的耦合度。