一、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]);
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/242690.html