分配JavaScript事件处理程序的方式如何影响其执行?

368yc8dk  于 2023-05-05  发布在  Java
关注(0)|答案(4)|浏览(114)

请考虑以下代码。

<!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>

通过点击按钮,我看到:

  1. A.onclickB.onclick被 Package 在“function onclick(event){...}”中。
    1.在B.onclick中,this是窗口(而不是按钮)。
    是否有其他考虑?
6ie5vjzr

6ie5vjzr1#

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

执行差异
#1this属性代码中的值绑定到JavaScript with的元素。在内联代码中,函数(或任何全局变量)首先从元素中搜索。如果找不到(通常是这种情况),内联侦听器将从元素的原型链中搜索该函数。如果没有找到匹配的属性名,搜索将到达window,并运行调用的全局函数。但如果函数名与查找路径上的任何属性名冲突,则会引发错误,或者执行意外的函数。

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

function action () {
  console.log('Not a function!?');
}
<form action="" method="post">
  <input onclick="console.log(action); action();">
</form>

#2属性代码的返回值实际上用于特定事件(如onsubmit)。返回false将阻止事件的默认操作。来自附加了addEventListener的侦听器的返回值总是被完全忽略(该值没有接收者)。
#3处理程序中使用的所有变量和函数必须是全局可访问的。这也算是一个破绽。
经常被误解的行为

在属性代码中调用的函数不是实际的事件处理程序函数,属性本身中的代码是附加的处理程序。因此,事件对象和正确的this值仅存在于属性处理程序代码中。如果在全局函数中需要这些值中的任何一个,则必须在调用全局函数时手动传递它们。
这是任何JavaScript代码的正常行为,在addEventListener附带的事件处理程序中也是如此。如果你从一个事件处理程序调用另一个函数,你必须传递事件对象,并绑定/传递this值,如果其他函数需要这些值。
从事件侦听器调用另一个函数的示例。

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值的处理方式没有区别。当事件触发时,在内联侦听器中,属性中的代码是第一个执行的代码,而对于其他附加类型,第一个执行的代码是处理程序函数本身。(示例中的事件对象部分是不相关的,因为几乎所有浏览器当前都实现了全局事件对象。global event object目前已弃用(2023年),因此这将是未来的一个问题。

inline listener中的缺陷
#1一个元素只能附加一个同类型的监听器。
#2内联监听器是潜在的攻击载体,因为属性中的监听器代码和从属性代码调用的任何函数都很容易被DevTools覆盖。
#3当编写内联侦听器时,正确引用字符串是复杂的。在服务器上编写动态标记时,引用的复杂性会增加,因为您必须处理HTML引用,JS引用和服务器端语言引用。
#4内联监听器不能在模块和浏览器扩展中工作(这些环境不在全局命名空间中,并且您不能从内联监听器调用模块或插件代码中编写的函数),并且不被许多框架接受,并且它们不会通过任何安全审计。
#5内联侦听器通过混合页面的表示层和操作层来打破Separation of concerns原则。
element.onxxxx

onxxxx属性没有缺陷#3,#4和#5,但是你不能用onxxxx属性添加一个以上的监听器,而且因为元素是全局的,所以监听器很容易用DevTools覆盖。

addEventListener

结论如下:使用addEventListener将事件附加到HTML元素,它没有任何缺陷。this被正确绑定,事件对象被正确传递,可以附加多个相同类型的事件,并且没有安全风险(当处理程序不是全局可访问的函数时),因为一切都发生在封装的代码中,而不需要单个全局变量或函数。
作为奖励,您可以选择触发事件的阶段,将仅触发一次的事件附加到元素而无需额外的工作,并获得更好的滚动某些事件的性能。

ctehm74n

ctehm74n2#

当从on-event处理程序调用代码时,它的this被设置为放置侦听器的DOM元素:

<!-- this will be the button -->
<button onclick="alert(this.tagName.toLowerCase());">
    Show this
</button>

但是请注意,只有外部代码才以这种方式设置this。在这种情况下,内部函数的this没有设置,所以它返回全局/窗口对象(即non–strict模式中的默认对象,其中调用未设置this)。
在全局上下文和函数上下文中检查this

<!-- this will be the global object which is window in the browser -->
<button onclick="alert((function() { return this; })());">
    Show inner this
</button>

另一个考虑因素是当您使用addEventListener时。
检查带有匿名函数和箭头函数的事件侦听器,并在箭头函数中使用this
请注意,虽然anonymous和arrow函数类似,但它们具有不同的this绑定。虽然anonymous(以及所有传统的JavaScript函数)创建自己的this绑定,但箭头函数继承了包含上下文的this绑定。

<button id="my_button" onclick="alert(this.tagName.toLowerCase());">
    Show this
</button>

<script>
    const btn = document.getElementById("my_button")
    btn.addEventListener("click", function () {
        // this will be the button
        console.log(this)
    })
    btn.addEventListener("click", () => {
        // this will be window
        console.log(this)
    })
</script>

这里有一篇我写的关于JavaScript this keyword的简单文章,但它没有提到事件处理程序。

lnvxswe2

lnvxswe23#

还有另一个考虑。考虑以下代码。

<!DOCTYPE html>
<title>Answer</title>
<script type=module>function f() {alert(1)}</script>
<input type=button value=1 onclick=f()>
<input type=button value=2 id=button>
<script type=module>button.onclick = function () {alert(2)}</script>

f在onclick中未定义,因为值为1的按钮(因为f是在模块中定义的)。因此,当事件处理程序必须执行模块中的代码(例如,使用import的代码)时,必须在模块中分配事件处理程序。

xghobddn

xghobddn4#

在HTML中设置事件侦听器没有什么特别的好处。此外,它被认为是有害的,例如CSP禁止它https://developers.google.com/web/fundamentals/security/csp#inline_code_is_considered_harmful

相关问题