ホーム » Javascript » EventEmitterをちょっと改造

EventEmitterをちょっと改造

EventEmitterはDOMを使用せずにイベント機能をクラスに与えることが可能なとても便利なクラス。
但し最大の不満はListener登録時にListener内でのthisを指定できない点。
※バージョンは5.2.4

例)リスナーの登録
[code]
var eventEmitter = new EventEmitter();
・・・
eventEmitter.addListener(‘click’, this.on_clicked.bind(this));
[/code]
上記のようにListener登録してしまうとremoveListenerが正しく機能せずにListenerが残ってしまう。
例)リスナーの削除
[code]
eventEmitter.removeListener(‘click’, this.on_clicked.bind(this));
[/code]

これらの問題を対応するためにEventEmitterを以下に従って修正

addListenerの修正

変更前
[code]
proto.addListener = function addListener(evt, listener) {
if (!isValidListener(listener)) {
throw new TypeError(‘listener must be a function’);
}

var listeners = this.getListenersAsObject(evt);
var listenerIsWrapped = typeof listener === ‘object’;
var key;

for (key in listeners) {
if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
listeners[key].push(listenerIsWrapped ? listener : {
listener: listener,
once: false
});
}
}

return this;
};
[/code]

変更後
[code]
proto.addListener = function addListener(evt, listener) {
if (!isValidListener(listener)) {
throw new TypeError(‘listener must be a function’);
}
// tarumi custom
var bind_instance = null;
if(arguments.length > 2){
bind_instance = arguments[2];
}
// tarumi custom

var listeners = this.getListenersAsObject(evt);
var listenerIsWrapped = typeof listener === ‘object’;
var key;

for (key in listeners) {
if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
listeners[key].push(listenerIsWrapped ? listener : {
listener: listener,
// tarumi custom
instance: bind_instance,
// tarumi custom
once: false
});
}
}

return this;
};
[/code]

関数:emitEventの修正

変更前
[code]
proto.emitEvent = function emitEvent(evt, args) {
var listenersMap = this.getListenersAsObject(evt);
var listeners;
var listener;
var i;
var key;
var response;

for (key in listenersMap) {
if (listenersMap.hasOwnProperty(key)) {
listeners = listenersMap[key].slice(0);

for (i = 0; i < listeners.length; i++) {
// If the listener returns true then it shall be removed from the event
// The function is executed either with a basic call or an apply if there is an args array
listener = listeners[i];

if (listener.once === true) {
this.removeListener(evt, listener.listener);
}
response = listener.listener.apply(this, args || []);

if (response === this._getOnceReturnValue()) {
this.removeListener(evt, listener.listener);
}
}
}
}

return this;
};
[/code]

変更後
[code]
proto.emitEvent = function emitEvent(evt, args) {
var listenersMap = this.getListenersAsObject(evt);
var listeners;
var listener;
var i;
var key;
var response;

for (key in listenersMap) {
if (listenersMap.hasOwnProperty(key)) {
listeners = listenersMap[key].slice(0);

for (i = 0; i < listeners.length; i++) {
// If the listener returns true then it shall be removed from the event
// The function is executed either with a basic call or an apply if there is an args array
listener = listeners[i];

if (listener.once === true) {
this.removeListener(evt, listener.listener);
}

if(listener.instance===null || listener.instance === ‘null’ || listener.instance === ‘undefined’ || listener.instance === undefined){
response = listener.listener.apply(this, args || []);
}
else{
response = listener.listener.apply(listener.instance, args || []);
}

if (response === this._getOnceReturnValue()) {
this.removeListener(evt, listener.listener);
}
}
}
}

return this;
};
[/code]

リスナーの登録の変更
[code]
eventEmitter.addListener(‘click’, this.on_clicked,this);
[/code]

リスナーの削除の変更
[code]
eventEmitter.removeListener(‘click’, this.on_clicked);
[/code]