有很大的不同。
通过 HTML 事件属性设置的事件处理程序是我们进行事件处理的第一种方式——在 DOM 出现之前。这种设置事件的方式被称为 DOM Level 0(就像在有标准之前的事实标准一样)。当这是这样做的方式时(大约在 1995 年),这很好,因为我们别无选择。但是,属性值变成事件处理代码的方式是这样处理的:
HTML 元素上声明了一个事件属性,该属性的值是事件发生时应执行的 JavaScript 代码:
<input type="button" onclick="alert('You clicked me!')" value="Click me">
注意onclick 的值不是函数引用,只是要运行的松散代码。
这实际上是由浏览器通过在全局范围内创建一个函数来实现的,该函数充当所提供代码的包装器。我们可以在这里看到:
// Output the value of the onclick property.
// Note the value supplied in the HTML
// is wrapped in a function that we didn't create
alert(document.querySelector("input").onclick);
<input type="button" onclick="alert('You clicked me!')" value="Click me">
由于在全局包装函数中自动包装属性值,非直观的事情经常发生如下:
function foo(){
// This function is invoked by clicking the HTML input element
// so, we may reasonably expect that "this" would reference that
// element. But, as you'll see, it doesn't.
alert("You clicked the " + this.nodeName + " element.");
}
<input type="button" onclick="foo()" value="Click me">
上面报告了undefined,因为实际上,this 在该上下文中指的是全局window 对象,它没有nodeName 属性。但是,如果您不知道 Global wrapper(为什么会这样),这将非常令人困惑,因为 DOM 事件处理程序使用的 this 几乎总是引用导致事件触发的 DOM 元素。
当 DOM 级别 1 事件处理规范问世(1998 年)时,一种配置事件的新方法也出现了。我们现在有了代表 HTML 元素的对象,每个对象都有映射到元素属性的属性。出于这个原因,许多人(直到今天)仍然认为使用属性或对象属性在很大程度上是同一回事。但是,有一些重要的区别(I've written about 在我的另一个答案中:参见答案的后半部分),因为属性用于设置值,这可能会影响状态,但属性用于覆盖属性和设置状态。
因此,使用 DOM 事件处理,我们将执行以下操作,您将看到,设置事件回调,不是作为要执行的松散代码,而是通过存储对要在事件发生时调用的函数的引用发生。因为我们提供了函数,所以它具有我们存储它的 DOM 对象的范围,我们不再需要用 Global 包装松散的命令。这会导致this 绑定按预期工作:
// Just a reference to a function is used with event
// properties. Not "loose" code. And, because the function
// is actually being stored with the DOM element, this binding
// works as expected.
document.querySelector("input").onclick = foo;
function foo(){
// This function is invoked by clicking the HTML input element
// so, we may reasonably expect that "this" would reference that
// element. But, as you'll see, it doesn't.
alert("You clicked the " + this.nodeName + " element.");
}
<input type="button" value="Click me">
DOM 事件处理的另一个好处是我们将 JavaScript 内容与 HTML 内容分开(即关注点分离)。这是一个好处,但不是改变的动力。
现在,在解释了这两种事件注册机制之间的区别之后,故事还没有完成。 DOM 事件属性仍然存在一个问题,如果你想设置多个事件处理程序,你没有一个干净的方法来做到这一点,因为你只能在给定的属性中存储一个函数引用。因此,对于现代事件处理,我们使用.addEventListener(),它允许我们使用我们想要的事件注册尽可能多的事件回调,并且我们获得了额外的好处,即我们注册的回调将按照我们注册的顺序被调用他们。
// Register an event listener:
document.querySelector("input").addEventListener("click", foo1);
// Register more event listeners:
document.querySelector("input").addEventListener("click", foo3);
document.querySelector("input").addEventListener("click", foo2);
function foo1(){ console.log("Hello from foo1"); }
function foo2(){ console.log("Hello from foo2"); }
function foo3(){ console.log("Hello from foo3"); }
<input type="button" value="Click me">