一、JS观察者模式和发布订阅
JS观察者模式和发布订阅模式都属于一种基于事件的设计模式,而且代码实现上其实也差不多。最重要的区别在于在观察者模式中,观察者知道被观察对象的存在,而在发布订阅模式中,发布者和订阅者相互独立,互相不知道对方的存在。
举个例子,在一个页面中有一个订阅按钮,我们在点击按钮时需要做两件事情:一是执行具体的订阅逻辑,二是在适当的时候通知其他需要收到通知的部分。在发布订阅模式中,我们会建立一个事件中心,让订阅者在其中注册自己感兴趣的事件,而发布者发布事件时,则会告知事件中心,事件中心再把事件推送给订阅者;而在观察者模式中,我们则会新建一个对象,作为被观察的目标,它会持有所有需要接收通知的观察者的引用,当目标发生变化时,就会直接通知观察者。
/* 发布订阅模式 */
const Event = new Vue();
// 订阅者A
Event.$on('subscribe', () => {
console.log('subscribe');
});
// 发布者
Event.$emit('subscribe');
/* 观察者模式 */
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index >= 0) {
this.observers.splice(index, 1);
}
}
notifyObservers() {
for (let observer of this.observers) {
observer.update();
}
}
}
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`${this.name} has been notified.`);
}
}
const subject = new Subject();
const observer1 = new Observer('Observer1');
const observer2 = new Observer('Observer2');
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 通知观察者
subject.notifyObservers();
二、JS观察者模式 阮一峰
JS观察者模式是基于发布订阅模式的一种设计模式,在JS中比较常见,对象间的一种依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。观察者模式提供了一种对象设计,让主题和观察者之间松耦合,以增强应用的可重用性和可扩展性。
阮一峰老师在他的JavaScript设计模式一书中给出了一个经典的例子,展示了观察者模式的用法,我们可以通过代码实现了解它的工作原理。
/* 发布者 */
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index >= 0) {
this.observers.splice(index, 1);
}
}
notifyObservers() {
for (let observer of this.observers) {
observer.update();
}
}
}
/* 观察者 */
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`${this.name} has been notified.`);
}
}
const subject = new Subject();
const observer1 = new Observer('Observer1');
const observer2 = new Observer('Observer2');
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 通知观察者
subject.notifyObservers();
三、JS观察者模式怎么写
JS观察者模式在实现上比较简单,首先我们需要定义一个目标对象,它可以拥有多个观察者对象,并且能够在变化时通知所有观察者。观察者对象则需要定义一个update()方法,以响应目标对象的通知。
/* 定义目标对象 */
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index >= 0) {
this.observers.splice(index, 1);
}
}
notifyObservers() {
for (let observer of this.observers) {
observer.update();
}
}
/* 触发变化 */
triggerChange() {
// do something...
// ...
// 让所有观察者知道变化
this.notifyObservers();
}
}
/* 定义观察者对象 */
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`${this.name} has been notified.`);
}
}
const subject = new Subject();
const observer1 = new Observer('Observer1');
const observer2 = new Observer('Observer2');
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 执行变化
subject.triggerChange();
四、JS观察者模式的实现
JS观察者模式的实现过程中,需要注意以下几点:
1. 借助ES6的class语法,更易于描述出观察者和目标对象之间的关系。
2. 目标对象需要维护一个观察者数组,通过addObserver()方法添加观察者,并通过removeObserver()方法将不在需要通知的观察者移除。
3. 观察者必须拥有update()方法,以响应目标对象的通知。在Notify()函数中,所有的观察者都会被遍历,并逐个执行它们的update()函数。
/* 目标对象类 */
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index >= 0) {
this.observers.splice(index, 1);
}
}
notifyObservers() {
for (let observer of this.observers) {
observer.update();
}
}
triggerChange() {
console.log('The subject has changed.');
this.notifyObservers();
}
}
/* 观察者类 */
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`${this.name} has been notified.`);
}
}
const subject = new Subject();
const observer1 = new Observer('Observer1');
const observer2 = new Observer('Observer2');
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 触发变化
subject.triggerChange();
五、JS观察者模式once
为了避免重复调用观察者的update()方法,我们可以在Observer类中加入once属性,标识观察者只需要被通知一次。
/* 目标对象类 */
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index >= 0) {
this.observers.splice(index, 1);
}
}
notifyObservers() {
for (let observer of this.observers) {
observer.update();
if (observer.once) {
this.removeObserver(observer);
}
}
}
triggerChange() {
console.log('The subject has changed.');
this.notifyObservers();
}
}
/* 观察者类 */
class Observer {
constructor(name, once = false) {
this.name = name;
this.once = once;
}
update() {
console.log(`${this.name} has been notified.`);
}
}
const subject = new Subject();
const observer1 = new Observer('Observer1', true);
const observer2 = new Observer('Observer2');
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
// 触发变化
subject.triggerChange();
// 再次触发变化
subject.triggerChange();
六、JS观察者模式的项目
JS观察者模式在实际项目中较为常见,特别是在前端开发中,有许多场景可以用它来实现交互效果。
以购物车为例,当用户点击添加购物车按钮时,购物车图标需要相应变化,并且在购物车中需要显示添加的商品信息,这时候我们可以借助观察者模式来实现自动刷新购物车。
<!-- HTML部分 -->
<button id="add-button">添加购物车</button>
<div id="cart-icon">购物车图标</div>
<div id="cart-content">购物车内容</div>
<!-- JS部分 -->
/* 目标对象类 */
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index >= 0) {
this.observers.splice(index, 1);
}
}
notifyObservers() {
for (let observer of this.observers) {
observer.update();
}
}
triggerChange() {
console.log('The subject has changed.');
this.notifyObservers();
}
}
/* 观察者类 */
class Observer {
constructor(name, data) {
this.name = name;
this.data = data;
}
update() {
console.log(`${this.name} has been notified.`);
this.render();
}
/* 渲染购物车 */
render() {
let cartString = '';
for (let item of this.data) {
cartString += `${item.name} 数量:${item.quantity}
`;
}
document.getElementById('cart-content').innerHTML = cartString;
}
}
const subject = new Subject();
// 添加购物车按钮事件
document.getElementById('add-button').addEventListener('click', () => {
/* 添加商品到购物车 */
// do something...
const data = [
{name: '商品1', quantity: 2},
{name: '商品2', quantity: 1}
];
/* 通知购物车图标和内容刷新 */
subject.triggerChange();
});
// 设置购物车图标
const observer1 = new Observer('CartIcon', [
{name: '商品1', quantity: 2},
{name: '商品2', quantity: 1}
]);
// 添加购物车内容
const observer2 = new Observer('CartContent', [
{name: '商品1', quantity: 2},
{name: '商品2', quantity: 1}
]);
// 添加观察者
subject.addObserver(observer1);
subject.addObserver(observer2);
七、JS观察者模式源码
JS观察者模式在前端中很常见,并且已经被各大框架所接受和使用,例如Angular、React、Vue等。以下是Vue.js的源码中Observer类的定义。
/* Vue.js Observer类 */
class Observer {
constructor(value) {
this.value = value;
this.dep = new Dep();
// 对象属性监听
def(value, '__ob__', this);
if (Array.isArray(value)) {
// 数组监听
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
this.walk(value);
}
}
/* 遍历对象属性 */
walk(obj) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);