2. 定义**具体观察目标类**(ConcreteSubject)
```typescript
// ObserverPattern.ts
// 具体观察目标类
class ConcreteSubject implements Subject{
private observers: Observer[] = [];
// 添加观察者
public addObserver(observer: Observer): void {
console.log(observer, " is pushed~~");
this.observers.push(observer);
}
// 移除观察者
public deleteObserver(observer: Observer): void {
console.log(observer, " have deleted~~");
const idx: number = this.observers.indexOf(observer);
~idx && this.observers.splice(idx, 1);
}
// 通知观察者
public notifyObservers(): void {
console.log("notify all the observers ", this.observers);
this.observers.forEach(observer => {
// 调用 notify 方法时可以携带指定参数
observer.notify();
});
}
}
定义具体观察者类(ConcreteObserver)
// ObserverPattern.ts
// 具体观
class ConcreteObserver implements Observer{
constructor(private name: string) {}
notify(): void {
// 可以处理其他逻辑
console.log(${this.name} has been notified.);
}
}
4. 测试代码
```typescript
// ObserverPattern.ts
function useObserver(): void {
const subject: Subject = new ConcreteSubject();
const Leo = new ConcreteObserver("Leo");
const Robin = new ConcreteObserver("Robin");
const Pual = new ConcreteObserver("Pual");
const Lisa = new ConcreteObserver("Lisa");
subject.addObserver(Leo);
subject.addObserver(Robin);
subject.addObserver(Pual);
subject.addObserver(Lisa);
subject.notifyObservers();
subject.deleteObserver(Pual);
subject.deleteObserver(Lisa);
subject.notifyObservers();
}
useObserver();
完整演示代码如下:
// ObserverPattern.ts
interface Subject {
addObserver: (observer: Observer) => void;
deleteObserver: (observer: Observer) => void;
notifyObservers: () => void;
}
interface Observer {
notify: () => void;
}
class ConcreteSubject implements Subject{
private observers: Observer[] = [];
public addObserver(observer: Observer): void {
console.log(observer, " is pushed~~");
this.observers.push(observer);
}
public deleteObserver(observer: Observer): void {
console.log(observer, " have deleted~~");
const idx: number = this.observers.indexOf(observer);
~idx && this.observers.splice(idx, 1);
}
public notifyObservers(): void {
console.log("notify all the observers ", this.observers);
this.observers.forEach(observer => {
// 调用 notify 方法时可以携带指定参数
observer.notify();
});
}
}
class ConcreteObserver implements Observer{
constructor(private name: string) {}
notify(): void {
// 可以处理其他逻辑
console.log(`${this.name} has been notified.`);
}
}
function useObserver(): void {
const subject: Subject = new ConcreteSubject();
const Leo = new ConcreteObserver("Leo");
const Robin = new ConcreteObserver("Robin");
const Pual = new ConcreteObserver("Pual");
const Lisa = new ConcreteObserver("Lisa");
subject.addObserver(Leo);
subject.addObserver(Robin);
subject.addObserver(Pual);
subject.addObserver(Lisa);
subject.notifyObservers();
subject.deleteObserver(Pual);
subject.deleteObserver(Lisa);
subject.notifyObservers();
}
useObserver();
前言
在之前两篇自测清单中,和大家分享了很多 JavaScript 基础知识,大家可以一起再回顾下~
本文是我在我们团队内部“现代 JavaScript 突击队”分享的一篇内容,第二期学习内容为“设计模式”系列,我会将我负责分享的知识整理成文章输出,希望能够和大家一起温故知新!
“现代 JavaScript 突击队”学习总结:
一、模式介绍
1. 背景介绍
在软件系统中经常碰到这类需求:当一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。这是建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。
我们将发生改变的对象称为观察目标,将被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,之后可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的产生背景。
2. 概念介绍
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式是一种对象行为型模式。
3. 生活场景
在所有浏览器事件(鼠标悬停,按键等事件)都是观察者模式的例子。
另外还有:
如我们订阅微信公众号“前端自习课”(观察目标),当“前端自习课”群发图文消息后,所有公众号粉丝(观察者)都会接收到这篇文章(事件),这篇文章的内容是发布者自定义的(自定义事件),粉丝阅读后作出特定操作(如:点赞,收藏,关注等)。
二、模式特点
1. 模式组成
在观察者模式中,通常包含以下角色:
2. UML 类图
图片来源:《TypeScript 设计模式之观察者模式》
3. 优点
4. 缺点
三、使用场景
在以下情况下可以使用观察者模式:
四、实战示例
1. 简单示例
// 观察目标接口 interface Subject { addObserver: (observer: Observer) => void; deleteObserver: (observer: Observer) => void; notifyObservers: () => void; }
// 观察者接口 interface Observer { notify: () => void; }
// 具体观 class ConcreteObserver implements Observer{ constructor(private name: string) {}
notify(): void { // 可以处理其他逻辑 console.log(
${this.name} has been notified.
); } }完整演示代码如下:
2. Vue.js 数据双向绑定实现原理
在 Vue.js 中,当我们修改数据状时,视图随之更新,这就是 Vue.js 的双向数据绑定(也称响应式原理),这是 Vue.js 中最独特的特性之一。 如果你对 Vue.js 的双向数据绑定还不清楚,建议先阅读官方文档《深入响应式原理》章节。
2.1 原理介绍
在官网中提供这么一张流程图,介绍了 Vue.js 响应式系统的整个流程:
图片来自:Vue.js 官网《深入响应式原理》
在 Vue.js 中,每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”(“Touch” 过程)过的数据 property 记录为依赖(Collect as Dependency 过程)。之后当依赖项的 setter 触发时,会通知 watcher(Notify 过程),从而使它关联的组件重新渲染(Trigger re-render 过程)——这是一个典型的观察者模式。
这道面试题考察面试者对 Vue.js 底层原理的理解、对观察者模式的实现能力以及一系列重要的JS知识点,具有较强的综合性和代表性。
2.2 组成部分
在 Vue.js 数据双向绑定的实现逻辑中,包含三个关键角色:
这三者的配合过程如图所示: 图片来自:掘金小册《JavaScript 设计模式核⼼原理与应⽤实践》
2.3 实现核心代码 observer
首先我们需要实现一个方法,这个方法会对需要监听的数据对象进行遍历、给它的属性加上定制的
getter
和setter
函数。这样但凡这个对象的某个属性发生了改变,就会触发setter
函数,进而通知到订阅者。这个setter
函数,就是我们的监听器:下面实现订阅者
Dep
:现在我们可以改写
defineReactive
中的setter
方法,在监听器里去通知订阅者了:五、总结
观察者模式又称发布-订阅模式、模型-视图模式、源-监听器模式或从属者模式。是一种对象行为型模式。其定义了一种对象间的一对多依赖关系,当观察目标发生状态变化,会通知所有观察者对象,使它们自动更新。
在实际业务中,如果一个对象的行为依赖于另一个对象的状态。或者说当目标对象的状态发生改变时,会直接影响到观察者的行为,尽量考虑到使用观察者模式来实现。
六、拓展
观察者模式和发布-订阅模式两者很像,但其实区别比较大。例如:
下一篇文章见。
参考文章
1.《3. 观察者模式》 2.《TypeScript 设计模式之观察者模式》 3.《JavaScript 设计模式核⼼原理与应⽤实践》