2

我已经使用 MFC 为 GUI 按钮编写了 C++ 事件驱动应用程序,并且我使用诸如 onmousedown 之类的 HTML 事件来触发网页中的一些 Javascript。当您使用界面并且事件是硬编码的时,事件驱动编程的想法很直观。

我知道如何在 C 中使用函数指针,并且了解 Windows 如何使用它们来运行事件循环。我可以编写代码来动态调用不同的函数。

我的问题是,从编译时转移到运行时。我在这里说的是任何语言,但是如果您需要一种语言,请选择 Javascript 或 PHP,因为这些天我最常使用它们。我完全不知道如何使用自己的事件对象来完成某事。这不是我得到的概念。而且由于我不使用它们,我不知道它们的实际用途可能是什么。

例如,我假设如果我想制作一个自定义的回合制游戏,我可能会使用事件对象来表示某个游戏片段能力的效果,该效果只需要“发生”。

有人可以给我简单地使用自定义事件吗?或者它有用或实用的情况?它是否很大程度上取决于语言和环境以及事件的处理方式?一个 Javascript 答案会很好,但我读过很多语言。

抱歉,我只是在这方面有欠缺,需要一些实用的、高级的(中级?)洞察力。这就像 C 中的指针。我“得到”指针以及它们如何成为你的朋友。但有一段时间我没有,我认识的很多人仍然没有。一旦你得到它就很简单,我只是不会以这种方式获得自定义的动态事件。

4

6 回答 6

5

当我们谈论面向事件的编程时,我们通常谈论的是观察者设计模式的一种或多种实现。观察者设计模式的一个目标是实现对象之间的松散耦合

如果没有一个好的例子,这可能并没有多大意义,而且肯定有很多好的例子;我怀疑我的会是他们中最好的,但我还是会尝试一下。想象两个物体。一个对象想知道另一个对象什么时候发生了什么事,所以它可以自己做一些事情——也许家里的狗 Rover 想在爸爸下班回家时在门口迎接他。有几种方法可以编程该场景,对吧?

  • 确保爸爸有对 Rover 的引用,当他回家时,调用 Rover 的 greetDatAtTheDoor() 方法,或者

  • 给 Rover 一个对 dad 的引用,监听 Dad 的 onArriveHome 事件,当它触发时,在内部调用 greetDadAtTheDoor()。

从表面上看,两者似乎没有太大区别,但实际上,爸爸对罗孚的一些执行情况有一些印象;如果出于某种原因必须更改 Rover 的实现,我们必须在两个地方进行更改:一次在 Rover 中,然后再次在 Dad 中。也许不是什么大不了的事,但仍然不理想。

现在想象妈妈也想和爸爸打招呼。而猫,Bigglesworth先生,他不太喜欢爸爸,想确保他不在身边,所以他宁愿出去。邻居乔想知道爸爸什么时候回家,这样他就可以因为爸爸欠他的二十美元而烦恼他。我们如何解释所有这些情况,以及不可避免地会出现的其他情况?将对妈妈的 greetHusband() 方法、猫的 getOutNow() 方法、狗的 greetDadAtTheDoor() 方法和 Joe 的 goGetMyMoney() 方法的引用放入爸爸的类定义中,这对爸爸来说很快就会搞砸——而且没有充分的理由,因为所有爸爸真正需要做的,就是自己回家。妈妈、狗、猫和邻居只想在发生这种情况时得到通知,

语言以不同的方式处理这些东西的细节,但是当你开始谷歌搜索时你会看到,这种模式通常涉及在事件“调度程序”上存在一个数组或类似数组的结构(在这种情况下,爸爸-- 即,其他人都感兴趣的对象)和“订阅者”(例如,妈妈、猫、Rover 和乔)都通过调用调度程序上公开的方法并传递对自己的引用来“注册” ——以某种形式出现在爸爸的“订阅者”数组中的引用。然后,当爸爸回家时,他“调度”了一个事件——“我到家了!” 比如说事件——本质上是一个函数,它循环遍历他的每个订阅者,并用他们自己的一些可公开访问的方法调用它们——只有它'

由于这些天我碰巧主要编写 ActionScript 代码,这就是它在我的世界中的样子——比如说,正如我在 Mom 类中声明的那样:

var dad:Dad = new Dad();
dad.addEventListener(DadEvent.ARRIVED_HOME, greetHusbandAtDoor);

private function greetHusbandAtDoor(event:DadEvent):void
{
   // Go greet my husband
}

那么,在爸爸身上,当我回家时,我所要做的就是:

dispatchEvent(new DadEvent(DadEvent.ARRIVED_HOME));

...并且因为 Flash 为我处理事件调度和通知细节(同样,每个人的做法都有些不同,但在内部,Flash 遵循我所描述的概念模型),我爸爸回家了,每个家庭成员都这样做了它注册后自动执行的操作。

希望这能解释一些事情——事件思考是什么样的,松散耦合意味着什么,为什么它很重要,等等。祝你好运!

于 2009-01-23T08:38:42.377 回答
2

在编译语言中,您编写自己的事件循环。在运行时语言中,它已经由宿主环境实现,程序员只得到一个抽象的接口进入事件系统。你永远不会看到事件循环。

使用事件构建功能有两个方面。首先是定义事件触发的条件。在浏览器中的 javascript 中,没有单个mousedown事件。事实上,页面上的每个元素都有一个。在这种情况下,它们中的任何一个何时触发的条件都基于鼠标光标的位置,以及鼠标按钮是否刚刚从向上状态切换到向下状态。这种情况可能会导致触发多个事件。(在浏览器中,有一个bubbling与此相关的称为事件的东西。但这超出了这里的范围)。

另一个方面是事件处理程序。无论是否有处理程序,事件都会发生。提供处理程序取决于您。在 javascript 中,函数是一等值。它们是在运行时定义的,而不是在编译时定义的。所以不需要指针或跳转表。您在运行时创建一个新函数,并将其分配给宿主环境在特定事件触发时查找的特殊属性。

所以在堆栈溢出时,每个向下箭头的事件都会有一个事件处理程序onclick,它将箭头变为红色,减少一个数字,检查一些其他变量并有条件地显示一个通知。然后当处理程序返回时,它将线程的控制权返回给浏览器,哈利路亚用户可以再次与浏览器交互。(处理事件时所有交互都会停止,因此您最好尽快处理)。这是一个使用 jquery 库的示例。

jQuery("a .downarrow")
.click(function(e){ e.target.style.color="red" /* ... do other things */ });

或者在原始 JavaScript(1.6 版)中,它可能是

Array.prototype.slice.apply(document.getElementsByTagName("a"))
.filter(function(o){ return !!(/downarrow/).exec(o.className)})
.forEach(function(o){ 
    o.onclick= function(e){ 
        e.target.style.color="red" /* ...do other things * / 
    }
 });

真的就是这么简单。你会做这样的事情的主要原因是它允许异步交互。GUI 应用程序不会一步一步地直线前进。它必须动态响应用户的输入。事件是一种伪造多线程应用程序外观的方法。它可以让用户看到许多事情正在同时发生而不会相互中断。

您可以使用库来定义自定义事件。这可能会编码一些特定领域的事件。例如:您有一个国际象棋游戏正在考虑下一步行动。搜索移动的过程超时。国际象棋游戏可以触发一个onComputerMove事件。您可以使用获取移动规范的函数来处理该事件,并更新棋盘的视觉表示,并为用户弹出一条消息,然后退出。然后,当玩家移动时,您可以触发一个onPlayerMove事件,它的作用大致相同。

于 2009-01-22T23:49:39.823 回答
1

事件最美妙的地方在于减少了两个模块或类之间的双向显式耦合。

所以假设我有两个班级:宝贝和妈妈


没有事件,我大致看到了两种让妈咪喂宝宝的方法:

  • 妈妈baby.pokeBelly()每半小时调用一次 Baby 上的方法,以了解您是否需要调用baby.fillWith(milk)
    问题是妈咪的线程要在一个while循环中baby之后等待一整天
  • Baby 知道其关联的 Mommy 并调用一个mommy.feed(me)方法,该方法又调用baby.fillWith(milk).
    那么问题来了,让一个简单的婴儿请合适的妈妈来填饱肚子。
    婴儿不这样做,因为他们应该保持简单。

使用事件,您可以将 a 绑定Mommy.OnBabyCries(baby)到其婴儿的baby.Cry事件:

void Mommy.OnBabyCries(baby) {
    baby.fillWith(milk)
}

在此之后,如果婴儿想吃东西,它所要做的就是提高它的 Cry 事件。

逻辑留在妈妈身上,甚至以后在任何保姆身上,宝贝都保持快乐,而不必担心。

于 2009-01-23T02:21:37.927 回答
0

我不知道我是否完全理解你的问题,但是一个带有事件的类就像一个带有按钮的 GUI。把它想象成一个homnculus......在每个有事件的班级里都有一个小人,有一个可以按下的虚拟按钮。他不知道按钮会做什么,但他知道什么时候按下它。现在想想一个相关的类,里面有另一个小人物。那个人知道,当他听到按下按钮的消息时,他应该做点什么。

每当您想让第二个人知道第一个人所做的事情时,您可以在第一类和第二类之间连接总机(订阅事件)。当第一个人按下按钮(触发事件)时,第二个人做他知道他应该做的任何事情......

我不知道这是否有帮助,或者我只是在想我的程序中的小人物。

于 2009-01-22T22:53:03.557 回答
0

StackOverflow,当您投反对票时,您的声望下降并且您的向下箭头变为红色。

通常,您编写一个onclick,更新其中的所有元素,并尽量不要忘记在每次页面布局更改时更改它。

相反,您可以声明onVoteDown事件、触发它onclick并订阅此事件:

  • 你的声誉div
  • 你的箭img
  • XmlHTTPRequest将您的投票发送到服务器。
于 2009-01-22T23:07:35.137 回答
0

我不完全确定您正在寻找什么样的答案,所以如果这不是很接近,我深表歉意。;)


自定义是指自定义操作自定义触发器还是真正自定义的事件

例如:自定义操作将响应预定义的事件(例如,鼠标单击),自定义触发器将通过脚本伪造鼠标单击,自定义事件将是未由语言定义或未定义的侦听器t 随时可用。

如果您正在寻找自定义事件,我真的无法帮助您。不过,我认为 JavaScript 对它们的支持并不多。


定义一个动作...

事件通常是通过对象的预定义属性建立的,比如window.onload。默认情况下,它们都等于undefinednull。因此,要使用它们,您必须将它们设置为函数的值。

您可以使用“匿名”功能:

window.onload = function (eventObject) {
    /* ... */
};

或者将它们设置为函数引用(不一样,但类似于指针):

function handleOnload(eventObject) {
    /* ... */
}

window.onload = handleOnload; /* note the lack of parenthesis */

当事件被触发时,函数被调用。因此,在任一设置中,当页面完成加载时,您放置的代码/* ... */将被执行。

还有更正式的方法可用于创建事件。W3C 定义addEventListener,而 IE 使用attachEvent. 更多信息

有很多参考资料可用于了解可用事件列表。这里有两个应该给你一个很好的基础:


浏览器通常处理事件的触发——浏览器事件 ( onload) 或用户事件 ( onclick)。属性和函数是用来响应的。

触发您自己的事件并不是最容易完成的事情。完全定义一个Event对象需要一些工作 - 并且因浏览器而异。

但是,如果您不需要该Event对象,则可以简单地调用该事件:

window.onload();
于 2009-01-22T23:31:35.837 回答