场景
当按下键盘上的加减键时,调整漫游速度,同时弹窗提醒。
因为漫游操作器封装成了一个对象,所以当速度改变时要通知外部调用这个对象者做成相应动作
初步代码思路:
如图所示
- 1.绑定好速度改变的回调函数
- 2.业务执行cameraWalkOperator.changeSpeed(value)
- 3.当速度改变时,会执行先前绑定好的操作步骤,弹窗显示
优化
以上代码虽然实现了需求场景,但是代码不太友好,也不好维护。试着进行如下抽象
- 1.创建一个listeners对象,对象上的每个属性代表一种监听类型。
- 2.listeners对象的type属性是一个数组,用来存放监听事件。
- 3.一二两步已经把监听事件绑定好了,现在就差触发。写一个触发函数,传入事件类型,查找监听的事件并执行。
以上代码虽然看起来多了复杂了,其实理解了并不复杂,就是最原生的对象和数组的结合使用。
想象一下场景,有三种不同类型的事件要监听触发不同的操作,如果采用一开始的代码思路,则要复制三个相同的代码,比较分散,代码也有冗余,耦合度较高。现在用一个对象包裹,不同的监听类型代表这个对象上的属性,对象上的属性值存在数组用来保存监听事件。这样所有的监听都在一个对象上,很集中,没有冗余,与外界解耦,相当于一个模块。
为了便于其它地方也能使用这个事件监听,把它封装成一个构造函数。
EventDispatcher构造函数
/**
* @author [Wu-Qin-Hao]
* @email [1539849378@qq.com]
* @create date 2020-08-03 10:55:00
* @modify date 2020-08-03 10:55:00
* @desc [自定义事件监听-观察者模式]
*/
function EventDispatcher() {}
Object.assign( EventDispatcher.prototype, {
// 添加事件监听
// @type 类型,用于区分查找
// @listener 函数,用于监听执行
addEventListener: function ( type, listener ) {
// 初始化_listeners为{}
if ( this._listeners === undefined ) this._listeners = {};
// 获取_listeners内容
var listeners = this._listeners;
// 如果_listeners对象上没有 type属性 的数据则初始化为[]
if ( listeners[ type ] === undefined ) {
listeners[ type ] = [];
}
// 如果_listeners对象的type属性值的数组里没有listener函数,则push进数组
if ( listeners[ type ].indexOf( listener ) === - 1 ) {
listeners[ type ].push( listener );
}
},
// 判断是否有这个事件监听
// @type 类型,用于区分查找
// @listener 函数,用于监听执行
hasEventListener: function ( type, listener ) {
// 如果_listeners未定义,则直接退出
if ( this._listeners === undefined ) return false;
// 获取_listeners内容
var listeners = this._listeners;
// 在_listeners对象上查找type属性,没有直接返回false,有则继续查找这个属性内容数组里是否有listener这个函数,有则返回true,否则返回false
return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
},
// 删除事件监听
// @type 类型,用于区分查找
// @listener 函数,用于监听执行
removeEventListener: function ( type, listener ) {
// 如果_listeners未定义,则直接退出
if ( this._listeners === undefined ) return;
// 获取_listeners内容
var listeners = this._listeners;
// _listeners对象上type属性的内容
var listenerArray = listeners[ type ];
// 如果有值
if ( listenerArray !== undefined ) {
// 查找listener监听函数是否存在
var index = listenerArray.indexOf( listener );
// 如果存在则在数组中删除
if ( index !== - 1 ) {
listenerArray.splice( index, 1 );
}
}
},
// 触发监听函数
dispatchEvent: function ( event ) {
// 如果_listeners未定义,则直接退出
if ( this._listeners === undefined ) return;
// 获取_listeners内容
var listeners = this._listeners;
// _listeners对象上type属性的内容
var listenerArray = listeners[ event.type ];
// 如果有值
if ( listenerArray !== undefined ) {
// 在event对象上增加target属性,把this对象传给target,后续外部调用者可能需要使用,先保存
event.target = this;
// slice方法并不会修改数组,而是返回一个新数组
var array = listenerArray.slice( 0 );
// 遍历event.type类型下的所有事件监听
for ( var i = 0, l = array.length; i < l; i ++ ) {
// 调用监听事件
array[ i ].call( this, event );
}
}
}
} );
export { EventDispatcher };
有关 构造函数,原型链。请看我的这篇文章 juejin.cn/post/684490…