我会尽量用最抽象的方式来解释。真正的实现可能要复杂得多。因此,我将要使用的名称是假设性的,但它们确实有助于解释事物,我希望 ;)
浏览器中的每个节点都是EventEmitter
类的实现 。此类维护一个对象events
,其中包含(键)的键:值对eventType
:一个包含listener
函数(值)的数组。
EventEmitter 类中定义的两个函数是addEventListener
和fire
。
class EventEmitter {
constructor(id) {
this.events = {};
this.id = id;
}
addEventListener(eventType, listener) {
if (!this.events[eventType]) {
this.events[eventType] = [];
}
this.events[eventType].push(listener);
}
fire(eventType, eventProperties) {
if (this.events[eventType]) {
this.events[eventType].forEach(listener => listener(eventProperties));
}
}
}
addEventListener
程序员使用它来注册他们想要的listener
函数,以便在执行他们想要的eventType
.
请注意,对于每个 distinct eventType
,都有一个不同的数组。该数组可以listener
为同一个eventType
.
fire
由浏览器调用以响应用户交互。浏览器知道进行了何种交互以及在哪个节点上进行了交互。它使用该知识fire
在适当的节点上调用适当的参数,即eventType
和eventProperties
。
fire
循环遍历与特定 eventType 关联的数组。遍历数组,它listener
在传递eventProperties
给数组时调用数组中的每个函数。
这就是listener
仅使用特定 eventType 注册的函数被调用一次的fire
方式。
下面是一个演示。在这个演示中有 3 个演员。程序员、浏览器和用户。
let button = document.getElementById("myButton"); // Done by the Programmer
let button = new EventEmitter("myButton"); // Done by the Browser somewhere in the background.
button.addEventListener("click", () =>
console.log("This is one of the listeners for the click event. But it DOES NOT need the event details.")
); // Done By the Programmer
button.addEventListener("click", e => {
console.log(
"This is another listener for the click event! However this DOES need the event details."
);
console.log(e);
}); // Done By the Programmer
//User clicks the button
button.fire("click", {
type: "click",
clientX: 47,
clientY: 18,
bubbles: true,
manyOthers: "etc"
}); // Done By the Browser in the background
用户点击按钮后,浏览器调用fire
按钮,将“click”作为 aneventType
和持有eventProperties
. 这会导致调用listener
“click”下的所有注册函数eventType
。
正如你所看到的,浏览器总是把eventProperties
着火。作为程序员,您可能会也可能不会在您的listener
函数中使用这些属性。
我发现对 stackoveflow 有帮助的一些答案:
使用 addEventListener 注册的事件存储在哪里?
Javascript 事件处理程序存储在哪里?