在 JavaScript 中不使用 DOM 事件的自定义事件模型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15308371/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Custom Events Model without using DOM events in JavaScript
提问by NTDave
I'm new to JavaScript and programming in general, and I have some questions about objects and events.
我是 JavaScript 和编程的新手,我对对象和事件有一些疑问。
Say I have an object:
说我有一个对象:
var computer = {
keyboard: {}
}
What I'm looking for is a way to register events to the keyboard object:
我正在寻找的是一种将事件注册到键盘对象的方法:
computer.keyboard.registerEvent( "keyEscape" );
Fire the event:
触发事件:
computer.keyboard.dispatchEvent( "keyEscape" );
And create event handlers:
并创建事件处理程序:
computer.keyboard.addEventListener( "keyEscape", function() {...} );
I know how to do this with DOM elements but not objects. Is this something that can be done in JavaScript (maybe with the help of JQuery)?
我知道如何使用 DOM 元素而不是对象来做到这一点。这是可以在 JavaScript 中完成的事情吗(也许在 JQuery 的帮助下)?
Even the slightest bit of guidance would be appreciated greatly.
即使是最轻微的指导也将不胜感激。
回答by Mohsen
If you want to make a completely stand alone event system without relying on DOM events you can have something like this using reactor pattern
如果你想制作一个完全独立的事件系统而不依赖于 DOM 事件,你可以使用反应器模式来做这样的事情
function Event(name){
this.name = name;
this.callbacks = [];
}
Event.prototype.registerCallback = function(callback){
this.callbacks.push(callback);
}
function Reactor(){
this.events = {};
}
Reactor.prototype.registerEvent = function(eventName){
var event = new Event(eventName);
this.events[eventName] = event;
};
Reactor.prototype.dispatchEvent = function(eventName, eventArgs){
this.events[eventName].callbacks.forEach(function(callback){
callback(eventArgs);
});
};
Reactor.prototype.addEventListener = function(eventName, callback){
this.events[eventName].registerCallback(callback);
};
Use it like DOM events model
像 DOM 事件模型一样使用它
var reactor = new Reactor();
reactor.registerEvent('big bang');
reactor.addEventListener('big bang', function(){
console.log('This is big bang listener yo!');
});
reactor.addEventListener('big bang', function(){
console.log('This is another big bang listener yo!');
});
reactor.dispatchEvent('big bang');
回答by Torben
If you don't want to implement your own event handling mechanisms, you might like my approach. You'll get all the features you know from usual DOM Events (preventDefault() for example) and I think it's more lightweight, because it uses the already implemented DOM event handling capabilities of the browser.
如果您不想实现自己的事件处理机制,您可能会喜欢我的方法。您将从通常的 DOM 事件(例如,preventDefault())中获得您所知道的所有功能,而且我认为它更轻量级,因为它使用了浏览器已经实现的 DOM 事件处理功能。
Just create a normal DOM EventTarget object in the constructor of your object and pass all EventTarget interface calls to the DOM EventTarget object:
只需在对象的构造函数中创建一个普通的 DOM EventTarget 对象,并将所有 EventTarget 接口调用传递给 DOM EventTarget 对象:
var MyEventTarget = function(options) {
// Create a DOM EventTarget object
var target = document.createTextNode(null);
// Pass EventTarget interface calls to DOM EventTarget object
this.addEventListener = target.addEventListener.bind(target);
this.removeEventListener = target.removeEventListener.bind(target);
this.dispatchEvent = target.dispatchEvent.bind(target);
// Room your your constructor code
}
// Create an instance of your event target
myTarget = new MyEventTarget();
// Add an event listener to your event target
myTarget.addEventListener("myevent", function(){alert("hello")});
// Dispatch an event from your event target
var evt = new Event('myevent');
myTarget.dispatchEvent(evt);
There is also a JSFiddle snippetto test it with your browser.
还有一个JSFiddle 片段可以用你的浏览器测试它。
回答by Jordan Foreman
Necroposting a little here, but I just wrote something like this last night - super simple, and based off of Backbone.js Events module:
这里有一点死尸,但我昨晚刚刚写了这样的东西 - 超级简单,并且基于 Backbone.js 事件模块:
EventDispatcher = {
events: {},
on: function(event, callback) {
var handlers = this.events[event] || [];
handlers.push(callback);
this.events[event] = handlers;
},
trigger: function(event, data) {
var handlers = this.events[event];
if (!handlers || handlers.length < 1)
return;
[].forEach.call(handlers, function(handler){
handler(data);
});
}
};
This approach is incredibly simple and extensible, allowing you to build a more sophisticated event system on top of it if you need.
这种方法非常简单且可扩展,允许您根据需要在其上构建更复杂的事件系统。
Using the EventDispatcher
is as simple as:
使用EventDispatcher
非常简单:
function initializeListeners() {
EventDispatcher.on('fire', fire); // fire.bind(this) -- if necessary
}
function fire(x) {
console.log(x);
}
function thingHappened(thing) {
EventDispatcher.trigger('fire', thing);
}
With some simple namespacing, you'll be able to pass basic events between modules with ease!
通过一些简单的命名空间,您将能够轻松地在模块之间传递基本事件!
回答by Philipp Munin
You can do it using JQuery.
您可以使用 JQuery 来完成。
For subscribing to your custom event:
订阅您的自定义事件:
$(computer.keyboard).on('keyEscape', function(e){
//Handler code
});
For throwing your custom event:
用于抛出您的自定义事件:
$(computer.keyboard).trigger('keyEscape', {keyCode:'Blah blah'});
Might be not the nicest way to do this, but you also can create functions in your method (addEventListener, dispatchEvent,...) that will wrap JQuery logic, to support both native looking api and JQuery.
可能不是最好的方法,但您也可以在您的方法中创建函数(addEventListener、dispatchEvent...)来包装 JQuery 逻辑,以支持本机外观的 api 和 JQuery。
回答by Igwe Kalu
Most likely, you need an event mechanism as a medium of communication among several objects.
很可能,您需要一个事件机制作为多个对象之间的通信媒介。
Heres how you can achieve that:
以下是您如何实现这一目标:
/**
* EventfulObject constructor/base.
* @type EventfulObject_L7.EventfulObjectConstructor|Function
*/
var EventfulObject = function() {
/**
* Map from event name to a list of subscribers.
* @type Object
*/
var event = {};
/**
* List of all instances of the EventfulObject type.
* @type Array
*/
var instances = [];
/**
* @returns {EventfulObject_L1.EventfulObjectConstructor} An `EventfulObject`.
*/
var EventfulObjectConstructor = function() {
instances.push(this);
};
EventfulObjectConstructor.prototype = {
/**
* Broadcasts an event of the given name.
* All instances that wish to receive a broadcast must implement the `receiveBroadcast` method, the event that is being broadcast will be passed to the implementation.
* @param {String} name Event name.
* @returns {undefined}
*/
broadcast: function(name) {
instances.forEach(function(instance) {
(instance.hasOwnProperty("receiveBroadcast") && typeof instance["receiveBroadcast"] === "function") &&
instance["receiveBroadcast"](name);
});
},
/**
* Emits an event of the given name only to instances that are subscribed to it.
* @param {String} name Event name.
* @returns {undefined}
*/
emit: function(name) {
event.hasOwnProperty(name) && event[name].forEach(function(subscription) {
subscription.process.call(subscription.context);
});
},
/**
* Registers the given action as a listener to the named event.
* This method will first create an event identified by the given name if one does not exist already.
* @param {String} name Event name.
* @param {Function} action Listener.
* @returns {Function} A deregistration function for this listener.
*/
on: function(name, action) {
event.hasOwnProperty(name) || (event[name] = []);
event[name].push({
context: this,
process: action
});
var subscriptionIndex = event[name].length - 1;
return function() {
event[name].splice(subscriptionIndex, 1);
};
}
};
return EventfulObjectConstructor;
}();
var Model = function(id) {
EventfulObject.call(this);
this.id = id;
this.receiveBroadcast = function(name) {
console.log("I smell another " + name + "; and I'm model " + this.id);
};
};
Model.prototype = Object.create(EventfulObject.prototype);
Model.prototype.constructor = Model;
// ---------- TEST AND USAGE (hopefully it's clear enough...)
// ---------- note: I'm not testing event deregistration.
var ob1 = new EventfulObject();
ob1.on("crap", function() {
console.log("Speaking about craps on a broadcast? - Count me out!");
});
var model1 = new Model(1);
var model2 = new Model(2);
model2.on("bust", function() {
console.log("I'm model2 and I'm busting!");
});
var ob2 = new EventfulObject();
ob2.on("bust", function() {
console.log("I'm ob2 - busted!!!");
});
ob2.receiveBroadcast = function() {
console.log("If it zips, I'll catch it. - That's me ob2.");
};
console.log("start:BROADCAST\n---------------");
model1.broadcast("crap");
console.log("end :BROADCAST\n---------------\n-\n-\n");
console.log("start:EMIT\n---------------");
ob1.emit("bust");
console.log("end:EMIT\n---------------");
<h1>...THE SHOW IS ON YOUR CONSOLE!</h1>
回答by Leon Rom
Here is a simple extension of Mohsen's answer, presented as a clear and short example.
这是Mohsen 答案的简单扩展,作为一个清晰而简短的示例呈现。
All his React functions are encapsulated into one React()
, added a function removeEventListener()
, and whole example is presented as one HTML file (or see it on JSFiddle).
他所有的 React 函数都被封装成一个React()
,添加了一个函数removeEventListener()
,整个示例以一个 HTML 文件的形式呈现(或在JSFiddle上查看)。
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<!--https://jsfiddle.net/romleon/qs26o3p8/-->
</head>
<body>
<script>
function Reactor() {
function Event(name) {
this.name = name;
this.callbacks = [];
}
Event.prototype.registerCallback = function(callback) {
this.callbacks.push(callback);
};
Event.prototype.unregisterCallback = function(callback) {
var array = this.callbacks,
index = array.indexOf(callback);
if (index > -1)
array.splice(index, 1);
}
this.events = {};
this.registerEvent = function(eventName) {
var event = new Event(eventName);
this.events[eventName] = event;
};
this.dispatchEvent = function(eventName, eventArgs) {
var events = this.events
if (events[eventName]) {
events[eventName].callbacks.forEach(function(callback) {
callback(eventArgs);
});
}
else
console.error("WARNING: can't dispatch " + '"' + eventName + '"')
};
this.addEventListener = function(eventName, callback) {
this.events[eventName].registerCallback(callback);
};
this.removeEventListener = function(eventName, callback) {
var events = this.events
if (events[eventName]) {
events[eventName].unregisterCallback(callback);
delete events[eventName];
}
else
console.error("ERROR: can't delete " + '"' + eventName + '"')
};
}
/*
demo of creating
*/
var reactor = new Reactor();
reactor.registerEvent('big bang');
reactor.registerEvent('second bang');
/*
demo of using
*/
log("-- add 2 event's listeners for 'big bang' and 1 for 'second bang'")
var callback1 = function() {
log('This is big bang listener')
}
reactor.addEventListener('big bang', callback1);
reactor.addEventListener('big bang', function() {
log('This is another big bang listener')
});
reactor.addEventListener('second bang', function() {
log('This is second bang!')
});
log("-- dipatch 'big bang' and 'second bang'")
reactor.dispatchEvent('big bang');
reactor.dispatchEvent('second bang');
log("-- remove first listener (with callback1)")
reactor.removeEventListener('big bang', callback1);
log("-- dipatch 'big bang' and 'second bang' again")
reactor.dispatchEvent('big bang');
reactor.dispatchEvent('second bang');
function log(txt) {
document.body.innerHTML += txt + '<br/>'
console.log(txt)
}
</script>
</body>
</html>