Шаблон наблюдателя
Паттерн Observer (наблюдатель) широко используется в языке javascript, события браузера (такие как щелчок мышью, событие клавиатуры keyDown) являются примерами этого паттерна. Основная причина разработки этого шаблона заключается в том, чтобы способствовать формированию низкой связанности, в этом шаблоне не просто объект вызывает объект, а один объект «подписывается» на активность другого объекта, когда состояние активности объекта изменения, просто уведомить подписчиков, а подписчиков еще называют наблюдателями.
Подписка на газету
Жизнь подобна походу в газету, чтобы подписаться на газету. Вы идете в газету, чтобы оплатить подписку на любую газету, которую вам нравится читать. Когда выходит новая газета, газета отправляет копию всем, кто подписался на нее. газета, и подписчики могут получить.
Мы можем использовать этот пример для имитации с помощью javascript. Предположим, что есть издатель Джек, который издает газету и журнал каждый день Подписчик Том будет уведомлен о новостях всякий раз, когда они появляются.
Джек должен иметь свойство subscribers, которое является типом массива, поведение подписки будет храниться в этом массиве по порядку, а под уведомлением подразумевается вызов метода объекта-подписчика. Поэтому, когда пользователь Том подписывается на сообщение, подписчик предоставляет один из своих методов для подписки () Джека. Конечно, вы также можете отказаться от подписки.Я больше не хочу читать газету, поэтому я вызываю функцию unsubscribe(), чтобы отменить подписку.
Простой шаблон наблюдателя должен иметь следующие члены:
- подписывается на массив
- subscribe() добавляет подписку в массив
- unsubscribe() удаляет подписку из массива
- publish() перебирает массив и вызывает метод подписки.
В этом режиме также требуется параметр типа, чтобы различать тип подписки. Например, некоторые люди подписываются на развлекательные новости, а некоторые — на спортивные журналы. Этот атрибут используется для пометки.
Делаем это простым кодом:
var Jack = {
subscribers: {
'any': []
},
//添加订阅
subscribe: function (type = 'any', fn) {
if (!this.subscribers[type]) {
this.subscribers[type] = [];
}
this.subscribers[type].push(fn); //将订阅方法保存在数组里
},
//退订
unsubscribe: function (type = 'any', fn) {
this.subscribers[type] =
this.subscribers[type].filter(function (item) {
return item !== fn;
}); //将退订的方法从数组中移除
},
//发布订阅
publish: function (type = 'any', ...args) {
this.subscribers[type].forEach(function (item) {
item(...args); //根据不同的类型调用相应的方法
});
}
};
Выше приведена реализация простейшего режима наблюдателя.Вы можете видеть, что код очень прост.Основной принцип заключается в том, чтобы хранить методы подписки в массиве по категориям, а затем извлекать и выполнять их при публикации.
Используйте Тома, чтобы подписаться ниже:
var Tom = {
readNews: function (info) {
console.log(info);
}
};
//Tom订阅Jack的报纸
Jack.subscribe('娱乐', Tom.readNews);
Jack.subscribe('体育', Tom.readNews);
//Tom 退订娱乐新闻:
Jack.unsubscribe('娱乐', Tom.readNews);
//发布新报纸:
Jack.publish('娱乐', 'S.H.E演唱会惊喜登台')
Jack.publish('体育', '欧国联-意大利0-1客负葡萄牙');
результат операции:
欧国联-意大利0-1客负葡萄牙
Практическое применение паттерна Observer
Видно, что режим наблюдателя делает отношения между двумя объектами очень слабыми.Когда отношения подписки не нужны, удалите оператор подписки. Так где же этот шаблон используется в практических приложениях?
модуль событий
События node.js — это модуль с высокой частотой использования.На нем основаны другие нативные модули node.js, такие как потоковая передача, HTTP и т. д. Мы можем написать версию основного кода событий, чтобы увидеть режим наблюдателя. практическое применение.
Функцией модуля событий является привязка событий, и все экземпляры, унаследованные от него, имеют возможность обрабатывать события. В первую очередь это класс, пишем его базовую структуру:
function EventEmitter() {
//私有属性,保存订阅方法
this._events = {};
}
//默认最大监听数
EventEmitter.defaultMaxListeners = 10;
module.exports = EventEmitter;
Давайте реализуем основные методы событий один за другим.
по методу
Первый — это метод on, который используется для подписки на события, в более старых версиях node.js — это метод addListener, это одна и та же функция:
EventEmitter.prototype.on =
EventEmitter.prototype.addListener = function (type, listener, flag) {
//保证存在实例属性
if (!this._events) this._events = Object.create(null);
if (this._events[type]) {
if (flag) {//从头部插入
this._events[type].unshift(listener);
} else {
this._events[type].push(listener);
}
} else {
this._events[type] = [listener];
}
//绑定事件,触发newListener
if (type !== 'newListener') {
this.emit('newListener', type);
}
};
Поскольку существуют другие подклассы, которые должны наследоваться от EventEmitter, необходимо решить, имеет ли подкласс атрибут _event, чтобы убедиться, что подкласс должен иметь этот атрибут экземпляра. Флаг flag — это флаг вставки метода подписки. Если он равен true, считается, что он вставлен в начало массива. Как видите, это реализация метода подписки шаблона Observer.
метод излучения
EventEmitter.prototype.emit = function (type, ...args) {
if (this._events[type]) {
this._events[type].forEach(fn => fn.call(this, ...args));
}
};
Метод emit должен взять метод подписки и выполнить его, а также использовать метод call для исправления указания this, чтобы он указывал на экземпляр подкласса.
один раз метод
EventEmitter.prototype.once = function (type, listener) {
let _this = this;
//中间函数,在调用完之后立即删除订阅
function only() {
listener();
_this.removeListener(type, only);
}
//origin保存原回调的引用,用于remove时的判断
only.origin = listener;
this.on(type, only);
};
Метод Once очень интересен, его функция заключается в том, чтобы подписаться на событие "один раз", и когда событие сработает, оно больше не сработает. Принцип заключается в том, чтобы обернуть метод подписки другим уровнем функции и удалить эту функцию после выполнения.
метод выключения
EventEmitter.prototype.off =
EventEmitter.prototype.removeListener = function (type, listener) {
if (this._events[type]) {
//过滤掉退订的方法,从数组中移除
this._events[type] =
this._events[type].filter(fn => {
return fn !== listener && fn.origin !== listener
});
}
};
Метод off — отписаться, принцип тот же, что и в режиме наблюдателя, просто удалить метод подписки из массива.
prependListener метод
EventEmitter.prototype.prependListener = function (type, listener) {
this.on(type, listener, true);
};
Излишне говорить об этом методе, вызовите метод on, чтобы передать отметку как истинную (вставьте метод подписки в заголовок).
Выше реализованы основные методы класса EventEmitter.
резюме
Создавая «наблюдаемые» объекты, когда происходит интересующее событие, все наблюдатели могут быть уведомлены об этом событии, что приводит к слабой связи.
Некоторые примеры относятся к "JavaScript Patterns" Автор: Стоян Стефанов