分配JavaScript事件处理程序的方式如何影响其执行? [英] How does the way in which a JavaScript event handler is assigned affect its execution?

查看:49
本文介绍了分配JavaScript事件处理程序的方式如何影响其执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下代码.

<!DOCTYPE html>
<title>Question</title>
<script>
    function report(handler, _this) {
        alert(
            "handler:\n" + handler + "\n" +
            "this: " + _this + "\n" +
            "event: " + event + "\n"
        )
    }
</script>
<input type=button id=A value=A onclick="report(A.onclick, this)">
<script>function f() {report(B.onclick, this)}</script>
<input type=button id=B value=B onclick="f()">
<input type=button id=C value=C>
<script>C.onclick = function () {report(C.onclick, this)}</script>

通过单击按钮,我已经看到:

By clicking the buttons, I have seen that:

  1. A.onclickB.onclick包裹在函数 onclick(event){...}".
  2. B.onclick中,this是窗口 (而不是按钮).
  1. A.onclick and B.onclick are wrapped in "function onclick(event) {...}".
  2. In B.onclick, this is the window (rather than a button).

还有其他考虑事项吗?

推荐答案

内联侦听器没有任何好处,相反,这是将事件侦听器添加到HTML元素的一种非常有缺陷的方法.

Inline listeners don't have any benefits, on the contrary, it's a very flawed way to add event listeners to HTML elements.

执行中的差异

#1 this值使用JavaScript with .在内联代码中,首先从元素中搜索称为函数(或任何全局变量)的代码.如果找不到(通常是这种情况),则内联侦听器从元素的祖先中搜索功能.如果找不到匹配的属性名称,则搜索到达window,并运行被调用的全局函数.但是,如果函数名称与查找路径上的任何属性名称冲突,则会引发错误或执行意外的函数.

#1 this value inside the attribute code is bound to the element with JavaScript with. In the inline code called function (or any global variable) is first searched from the element. If it's not found (which usually is the case), an inline listener searches the function from the ancestors of the element. If a matching property name is not found, the search reaches window, and runs the global function which was called. But if the function name conflicts with any property name on the lookup path, an error is fired, or an unexpected function is executed.

内联侦听器如何找到包装表单的action属性的示例,只需单击输入:

An example of how an inline listener finds action property of the wrapping form, just click the input:

function action () {
  console.log('Not a function!?');
}

<form action="" method="post">
  <input onclick="console.log(action); action();">
</form>

#2 属性代码的返回值实际上用于特定事件(如onsubmit).返回false将阻止事件的默认操作.始终完全忽略来自附加有onxxxxaddEventListener的侦听器的返回值(该值没有接收器).

#2 The return value of the attribute code is actually used in particular events (like onsubmit). Returning false prevents the default action of the event. The return value from a listener attached with onxxxx or addEventListener is always fully ignored (there's no receiver for the value).

#3 必须在全局范围内访问处理程序中使用的所有变量和函数.这也可以算作一个缺陷.

#3 All the variables and functions used in the handler must be globally accessible. This could be counted as a flaw too.

经常被误解的行为

在属性代码中调用的函数不是实际的事件处理程序函数,属性本身中的代码是附加的处理程序.因此,事件objet和正确的this值仅存在于属性处理程序代码中.如果您需要在全局函数中使用这些值之一,则必须在调用全局函数时手动传递它们.

A function called in the attribute code is not the actual event handler function, the code in the attribute itself is the attached handler. Hence the event objet and correct this value are present in the attribute handler code only. If you're going to need either of these values in a global function, you've to pass them manually when calling the global function.

这是任何JavaScript代码的正常行为,也在addEventListener附带的事件处理程序中.如果要从事件处理程序中调用另一个函数,则必须传递事件对象,并绑定/传递this值(如果该其他函数需要这些值).

This is normal behavior of any JavaScript code, also in an event handler attached with addEventListener. If you'll call another function from an event handler, you've to pass the event object, and bind/pass this value, if that other function needs these values.

从事件侦听器调用另一个函数的示例.

An example of calling another function from event listeners.

function show () {
  console.log('Called:', typeof event, this.toString());
}

const inp = document.querySelectorAll('input')[1];
inp.addEventListener('click', function (e) {
  console.log('addEventListener:', typeof e, this.toString());
  show();
});

<input onclick="console.log('Inline:', typeof event, this.toString()); show();" placeholder="Inline listener">
<input placeholder="addEventListener">

如我们所见,在附加类型之间没有什么区别-事件对象和this值的处理方式.当事件触发时,在内联侦听器中,属性中的代码是最先执行的代码,而对于其他附加类型,最先执行的代码是处理函数本身. (示例中的事件对象部分无关紧要,因为当前几乎所有浏览器都在实现全局事件对象.)

As we can see, there's no difference – between attaching types – how event object and this value are handled. When an event fires, in an inline listener the code in the attribute is the code first to execute, whereas with other attaching types, the first executed code is the handler function itself. (The event object part in the example is partially irrelevant, since almost all browsers are currently implementing the global event object.)

内联侦听器中的缺陷

#1 您只能将单个相同类型的侦听器附加到元素.

#1 You can attach only a single listener of the same type to an element.

#2 内联侦听器是潜在的攻击载体,因为属性中的侦听器代码以及从该属性代码调用的任何函数都易于使用DevTools覆盖.

#2 Inline listeners are potential attacking vectors, as both, the listener code in the attribute, and any function called from the attribute code are easy to override with DevTools.

#3 编写内联侦听器时,正确地引用字符串是很复杂的.在服务器上编写动态标签时,由于要注意HTML引用,JS引用和服务器端语言引用,因此引用的复杂性会增加.

#3 When writing an inline listener, quoting of the strings correctly is complex. Complexity of the quoting increases when writing a dynamic tag on the server, since you've to take care of the HTML quoting, JS quoting and the server-side language quoting.

#4 内联侦听器无法在模块和浏览器扩展中使用,并且未被许多框架接受,并且它们不会通过任何安全审核.

#4 Inline listeners are not working in modules and browser extensions, and are not accepted by many frameworks, and they won't pass any security audits.

#5 内联侦听器破坏了关注点分离原则,混合页面的表示层和动作层.

#5 Inline listeners break Separation of concerns principle by mixing presentation and action layers of the page.

element.onxxxx

onxxxx属性不受#3,#4和#5缺陷的影响,但是您不能使用onxxxx属性添加多个侦听器,而且由于元素是全局的,因此侦听器很容易被DevTools覆盖.

onxxxx property doesn't suffer from the flaws #3, #4 and #5, but you can't add more than a single listener with an onxxxx property, and since elements are global anyway, the listener is easy to override with DevTools.

addEventListener

结论:使用 addEventListener 将事件附加到HTML元素,它没有缺陷. this被正确绑定,正确地传递了事件对象,可以附加多个相同类型的事件,并且没有安全风险(当处理程序不是全局可访问的函数时),因为一切都在封装的代码中发生,而无需单个全局变量或函数.

As a conclusion: Use addEventListener to attach events to HTML elements, it has no flaws. this is correctly bound, event object is correctly passed, multiple same type of the events can be attached, and without security risks (when the handler is not a globally accessible function), because everything happens in encapsulated code, without needing a single global variable or function.

作为奖励,您可以选择触发事件的阶段,将一次触发的事件附加到元素上,而无需进行额外的工作,然后获得

As a bonus, you can choose the phase where the event is fired, attach only-once-triggering events to elements without extra work, and get better performance of scrolling with certain events.

这篇关于分配JavaScript事件处理程序的方式如何影响其执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆