釋出訂閱模式 觀察者模式
釋出訂閱是主動的,來個例子,我有一個女朋友,要包,要車。等我有錢了,買包買車。這個過程中,我要先訂閱買包買車,等有錢了在釋出。
觀察者被動執行。我先觀察你,你有變化了,我在變化,有點兒類似vue watch
釋出訂閱
例子1
先把想幹的事情存起來,當真的發生時候,依次將陣列中的內容執行。對應關係如下:
{
有錢: [買包,買車]
女生失戀: [哭,喝酒]
}
複製程式碼
程式碼實現:
let EventEmitter = require('events');
let util = require('util');
// 定義一個類
function Girl() {
}
// 繼承共有方法
util.inherits(Girl, EventEmitter);
let girl = new Girl();
let cry = () => {
console.log('哭');
}
let drink = () => {
console.log('喝酒');
}
// 繫結對應方法
girl.on('女生失戀', cry);
girl.on('女生失戀', drink);
// 預設情況下是不執行的
// 觸發執行
girl.emit('女生失戀');
複製程式碼
模擬實現釋出訂閱
- 定義一個類
<!--新建檔案events.js-->
// 定義一個類
function EventEmitter() {
}
// 匯出
module.exports = EventEmitter;
複製程式碼
- 定義好 on 和 emit 方法
// 定義一個類
function EventEmitter() {
}
// 訂閱
EventEmitter.prototype.on = function() {
}
// 釋出
EventEmitter.prototype.emit = function() {
}
// 匯出
module.exports = EventEmitter;
複製程式碼
- 首先先維護一個物件,物件對應
{ '女生失戀': ['喝酒fn', '哭fn'] }
:
// 定義一個類
function EventEmitter() {
// 維護一個物件
// {
// '女生失戀': ['哭', '喝酒']
// }
this._events = {};
}
// 訂閱
EventEmitter.prototype.on = function(type, callback) {
// 如果取不到_events 預設給空物件
if (!this._events){
this._events = {};
}
// 物件中存在
if (this._events[type]) {
this._events[type].push(callback);
} else {
this._events[type] = [callback];
}
}
複製程式碼
- 當執行 emit 時候將陣列中的函式依次執行
// 釋出
EventEmitter.prototype.emit = function(type) {
// 存在陣列
if (this._events[type]) {
this._events[type].forEach(fn => {
// 此處的 fn 就是 喝酒, 哭
fn();
});
}
}
複製程式碼
- 解綁方法
EventEmitter.prototype.removeListener = function(type, callback) {
// 找到陣列中對應方法移除。
if (this._events[type]) {
this._events[type] = this._events[type].filter(fn => {
return fn != callback;
})
}
}
// 外界呼叫
// 解綁事件
girl.removeListener('女生失戀', cry);
複製程式碼
- 新增 newListener 方法獲取當前監聽什麼事件名字。
當前監聽的如果不是 newListener 時候。執行 newListener 方法把當前 type 傳遞給回撥函式,這樣外界就可以獲取 type 了
// 訂閱
EventEmitter.prototype.on = function(type, callback) {
if (type !== 'newListener') {
this._events['newListener'].forEach(fn => {
fn(type);
})
}
// 如果取不到_events 預設給空物件
if (!this._events){
this._events = {};
}
// 物件中存在
if (this._events[type]) {
this._events[type].push(callback);
} else {
this._events[type] = [callback];
}
}
// 外界呼叫
girl.on('newListener', (type) => {
console.log(type);
})
複製程式碼
- 其他方法
// 預設最大監聽
EventEmitter.defaultMaxListener = 10;
// 設定最大監聽
EventEmitter.prototype.setMaxListeners = function(count) {
this._count = count;
}
// 獲取最大監聽
EventEmitter.prototype.getMaxListeners = function(count) {
return this._count || EventEmitter.defaultMaxListener;
}
// 獲取 eventName
EventEmitter.prototype.eventNames = function() {
return Object.keys(this._events);
}
// 獲取監聽方法
EventEmitter.prototype.listeners = function(type) {
return this._events[type];
}
// 移除所有監聽
EventEmitter.prototype.removeAllListeners = function(type) {
if (type) {
return this._events[type] = [];
};
this._events = {};
}
複製程式碼
程式碼:
event.js
// 定義一個類
function EventEmitter() {
// 維護一個物件
// {
// '女生失戀': ['哭', '喝酒']
// 'newListener': []
// }
this._events = {};
}
// 訂閱
EventEmitter.prototype.addListener = EventEmitter.prototype.on = function(type, callback) {
// 如果取不到_events 預設給空物件
if (!this._events) {
this._events = Object.create(null);
}
if (type !== 'newListener' && this._events['newListener'] && this._events['newListener'].length) {
this._events['newListener'].forEach(fn => {
fn(type);
})
}
// 物件中存在
if (this._events[type]) {
this._events[type].push(callback);
} else {
this._events[type] = [callback];
}
// 如果超限制提示錯誤
if (this._events[type].length === this.getMaxListeners()) {
console.warn('memory link detected');
}
}
// 釋出
EventEmitter.prototype.emit = function(type, ...args) {
// 存在陣列
if (this._events[type]) {
this._events[type].forEach(fn => {
// 此處的 fn 就是 喝酒, 哭
fn(...args);
});
}
}
EventEmitter.prototype.removeListener = function(type, callback) {
// 找到陣列中對應方法移除。
if (this._events[type]) {
this._events[type] = this._events[type].filter(fn => {
return fn != callback && fn.l !== callback;
})
}
}
// 預設最大監聽
EventEmitter.defaultMaxListener = 10;
// 設定最大監聽
EventEmitter.prototype.setMaxListeners = function(count) {
this._count = count;
}
// 獲取最大監聽
EventEmitter.prototype.getMaxListeners = function(count) {
return this._count || EventEmitter.defaultMaxListener;
}
// 獲取 eventName
EventEmitter.prototype.eventNames = function() {
return Object.keys(this._events);
}
// 獲取監聽方法
EventEmitter.prototype.listeners = function(type) {
return this._events[type];
}
// 移除所有監聽
EventEmitter.prototype.removeAllListeners = function(type) {
if (type) {
return this._events[type] = [];
};
this._events = {};
}
// once 先繫結 wrap 函式當執行完後從陣列中刪除。
EventEmitter.prototype.once = function(type, callback) {
// 新增一個包裹函式。
let wrap = (...args) => {
callback(...args);
this.removeListener(type, wrap);
}
// 將callback儲存在 wrap.l上
wrap.l = callback;
this.on(type, wrap);
}
// 匯出
module.exports = EventEmitter;
複製程式碼
呼叫:
let EventEmitter = require('./events');
let util = require('util');
// 定義一個類
function Girl() {
// EventEmitter.call(this);
}
// 繼承共有方法
util.inherits(Girl, EventEmitter);
let girl = new Girl();
let cry = (a, b) => {
console.log('哭', a, b);
}
let drink = (a, b) => {
console.log('喝酒', a, b);
}
// girl.setMaxListeners(1);
// console.log(girl.getMaxListeners());
girl.on('newListener', (type) => {
// console.log(type, '哈哈哈');
})
// 繫結對應方法
girl.once('女生失戀', cry);
// girl.on('女生失戀', drink);
// 解綁事件
// girl.removeListener('女生失戀', cry);
// 預設情況下是不執行的
// 觸發執行
girl.emit('女生失戀', 1, 2);
girl.emit('女生失戀');
girl.emit('女生失戀');
// 獲取最大監聽
// console.log(EventEmitter.defaultMaxListener);
// 獲取 eventNames [ 'newListener', '女生失戀' ]
// console.log(girl.eventNames());
console.log(girl.listeners('女生失戀'));
複製程式碼