这一切都非常...抽象。
这一切都很好。
为这样的抽象提出解决方案不一定是 SO 的目标,因为有一百万种方法可以做到这一点。
但是,有几个关键点:
除非您以某种方式将模拟代码链接到用户生成的事件,否则浏览器可能会阻止您的模拟接管用户
(想象一下浏览器让页面上的任何 JS 模拟任何按键或任何鼠标单击)时间)。
这通常意味着您受到严格限制,使用一些不适合消费者使用的库,使用用户必须安装/运行或破坏的特定于浏览器的插件(即:FireFox/Chrome/IE)。选择一个(或多个)。
带有回调的自定义事件通常允许您将程序分开,但让它们一起运行。
Zakas 做了一些关于沙盒的精彩演讲,但这些都是非常企业级的、最终游戏类型的东西。它们很棒,库构建者和系统工程师应该完全考虑它们,但是为了制作平均页面,最好完成你需要编写的 100 行代码,而不是用一个库构建一个完整的框架它既将每个模块包裹在一个外壳中,又将自身注入该模块中。
这就是 Pub-Sub(和 Observer)/Moderator(或 Mediator)发挥作用的地方。
根据库的不同,上述任何一个也可能被称为“发射器”或“生成器”等。
上面的每一个都是以基本相同的方式创建的,并且做同样的事情。
目标是将结果传达给想要聆听的听众。
演讲者选择何时通知听众,以及告诉他们什么。
观众选择随时收听,等待下一次广播(可能永远不会出现,但至少他们正在关注它),或者他们可以选择关闭并停止收听广播。
那么,它们之间的区别在于“说话者”如何知道正在发生的事情。
发布者-订阅者
在publisher-subscriber
中,说话者是使事情发生的对象。
看看推特。
您注册了一个 Twitter 帐户。拥有它后,您可以关注任何您想要的人。
每当他们发推文时,您都会收到有关它的通知。
任何人都可以关注您,因此无论何时您发布推文,他们都会收到通知。
执行该操作的人将该操作发布给任何想要听到它的订阅者。可能有 3000 个订阅者和一个发布者(一份时事通讯),或者 3000 个发布者和一个订阅者……可能有发布者不订阅,或者订阅者不发布……但这是范式。
观察者
在observer
中,您正在谈论与执行工作的事物耦合的对象。
可能很紧 它可能是松散的。但是有一个东西在做某事,并且有一个东西确切地知道它在做什么。然后人们会窃听观察者以获取更新。
想想棒球时代,人们通过广播收听比赛。
观察者将是无线电评论员。
他不是那个击球或偷垒的人。他是展台上的那个人,他看到了一切,知道这一切意味着什么,并将其转化为用户友好的信息,供所有在家收听的人使用。
如今,玩家可以在制作游戏时直接向所有粉丝发布推文pub-sub
(关闭更新谁是三垒之王(这一次,不是杰夫,在他狭窄的 Z28 中)。
调解员/主持人
在这种情况下,我们谈论的是每个人都知道的对象,但没有人知道彼此。
想象一个呼入式谈话广播节目。
每个人都知道这个节目。每个人都可以打电话到节目中,并与主持人交谈。但除了巧合之外,没有人知道其他听众的任何事情。
它与 有点不同pub-sub
,因为每个人都是发布者,但您不必知道某人的 Twitter 句柄就可以收到他们的消息。你可以说Hey Twitter, any time anybody in the world mentions #browns, let me know. I'm starving.
。
这有点不同,observer
因为当主持人看着做工作的人时,任何人都可以随时做工作。
哪一个是正确的?
这完全取决于您需要什么,以及您实际上打算用它做什么。
以下是我们如何制作版主:
var Moderator = function () {
var events = {},
notify = function (evtName, data) {
var evt = events[evtName];
if (!evt) { return; }
evt.forEach(function (func) { func(data); });
},
listen = function (evtName, callback) {
events[evtName] = events[evtName] || [];
events[evtName].push(callback);
},
ignore = function (evtName, callback) {
var evt = events[evtName];
if (!evt) { return; }
evt.forEach(function (func, i, arr) {
if (func === callback) { arr.splice(i, 1); }
});
};
return { ignore : ignore,
listen : listen,
notify : notify };
};
非常简单明了,对吧?当然,这并不是特别充满花里胡哨的东西,比如只订阅下一次事件触发,或者下一次 3 次,或者其他什么......
我们可能会这样使用它:
var Game = function () {
var game_moderator = Moderator(),
scoreboard = Scoreboard(),
messages = MessageBoard(),
player_one = Player(),
player_two = Player();
function initialize () {
player_one.initialize(game_moderator);
player_two.initialize(game_moderator);
game_moderator.listen("player:death", scoreboard.update);
game_moderator.listen("player:death", messages.add_kill);
game_moderator.listen("chat:input", messages.add_msg );
}
function start() {}
/* update... draw... etc... */
return {
load : load,
initialize : initialize,
start : start
};
};
var game = Game(),
loading = game.load();
loading.done(function () {
var initializing = game.initialize();
initializing.done(game.start);
});
同时,Player
可能看起来像这样:
var Player = function (name) {
var system,
health = 30,
damage = [],
attack = function () { /* ... */ },
hurt = function (amt, type, from) {
health -= amt;
damage.push({ amount : amt, type : type, from : from });
},
die = function () {
var killing_blow = damage[damage.length - 1];
killing_blow.player = name;
system.notify("player:death", killing_blow);
},
update = function () {
if (health <= 0) { die(); }
},
draw = function () {},
initialize = function (sys) { system = sys; };
return {
initialize : initialize,
update : update,
draw : draw,
hurt : hurt
/* ... */
};
};
回顾Game.initialize
函数,我们可以看到我们有一个记分牌和一个消息面板,它们都将处理“玩家:死亡”事件。
由于调用和定义玩家的方式,我在他们的初始化期间注入了对主持人的引用(以便我可以将所有内容分开:依赖注入)。
但是player_one
什么都不知道player_two
,scoreboard
什么都不知道,除了偶尔调用它的.update
方法,传入kill信息,messages
得到各种各样的爱,却是那种大家都陌生的……
回到你原来的问题:
如果你hidden-input
是通过监视按键来填写的,为什么不建立一个观察者呢?
构建一个连接到keyup
事件监听器和keydown
事件监听器的观察者。
让那个观察者把这些事件变成有用的信息(例如:当你按住一个键时,keydown
事件每秒触发几十次——你可能不希望这样......所以在添加新键时要警惕,或者释放按下的键时发出警报)。
订阅hidden-input
那个。
当hidden-input
已满,或者您的要求正在运行时... ...并且您想触发一个事件,请拥有一个全局主持人(或作为其中一部分的系统顶层的主持人hidden-input
) .
从那里,触发一个名为的事件"hidden-input-filled"
或任何有意义的事件。
关心这一事件的人可以通过版主订阅该事件。
也就是说,当然,如果你的程序是以这样一种方式构建的,没有人应该知道隐藏输入,但是有些人应该知道hidden-input
's 的事件。
如果只有一组选定的事物应该知道hidden-input
,并且这些是唯一应该知道它的事件的事物,并且hidden-input
也应该能够对它们有所了解,那么制作它们pub-sub
。
或者混合搭配你的联系:
这个想法是建立有意义的沟通,告诉人们他们需要知道的东西,仅此而已。
因此,如果 Twitter 用户应该是 sub-pub,但页面上的不同小部件(时间线、搜索和最近的图片等)不应该对彼此了解太多(当然不会让每张图片都能与每个时间线更新共享),然后创建一个全局主持人,整个小部件可以通过它与其他小部件进行通信(例如当时间线需要根据搜索结果更新时),并且在每个小部件内部,有一个主持人和/或发布订阅用于不同的连接,在组件。
希望这会有所帮助,我希望这能解释为什么通过这样做来设计一个松散耦合的大型程序比通过劫持真实事件然后发射假事件更容易,旨在针对程序的不同区域。
老实说,如果您的整个站点及其所有程序都归结为:“我有这个输入可以做这件事,另一个输入可以做另一件事”,答案是这真的无关紧要很多。
当您到达:“我有一个页面,其中有 8 个空间用于不同的小部件,并且有 16 个可能的小部件可以随时加载到这 8 个插槽中的任何一个中,并且在某些小部件中应该执行一些主要操作在其他小部件中引起响应,并且每个小部件需要在内部控制很多事件,我们需要在 AJAX 库和 DOM/MVC(或 MVVM)库中通过管道来控制内部发生的所有内容每个小部件,它自己,只有我一个……”
那时,敲定这些东西并敲定 Promises/Deferreds/Futures,并将您的大想法分解成更小的部分,分布在正在运行的应用程序生命周期的不同点上,这是一个好主意。