内联 onclick 如何评估?

7
我很好奇内联元素属性事件的内容是如何实现的。
我们从一个简单的函数开始。
function handler(e) {
    console.log(e);
}

使用案例1

如果我们想要将此处理程序添加到我们的按钮中,我们可以执行以下操作:

<button onclick="handler();">Click Me</button>

点击哪些日志:未定义

好的,我们调用了一个没有传递参数的函数。

用例2

如果我们现在将按钮更改为接受事件作为参数:

<button onclick="handler(event);">Click Me</button>

我们记录点击事件:MouseEvent{ ... }

现在我们记录事件,正如我们所预期的那样

用例3

但是如果我们只指定函数名称呢?

<button onclick="handler">Click Me</button>

什么都没有发生!我希望只是给出一个函数的引用就可以解决问题。因此,当“onclick”事件发生时,引用的函数将被调用并传递相应的事件参数。

用例4

当我们在JavaScript中添加处理程序时:

const btn = document.getElementById('btn');
btn.onclick = handler;

它起作用了,捕获事件并记录相关的event对象。

结束问题

我们将任何文本/内容/语句传递给onclick =""内联时,基本上会在其中运行eval()吗?当我们在javascript中附加它(btn.onclick =...)时,它通过正确的过程拦截事件,然后调用有限的处理程序函数,并将相应的事件对象传递给它(例如MouseEvent)?

简要说明

明确一点,这是一个信息性问题,旨在理解基础知识,而不考虑它是否被视为良好或不良实践。这一点没有被讨论。


1
我不认为会发生这种情况 - 看看这里 - Jack Bashford
1
请记住,对函数使用 with 可能会导致一些可怕的事情发生(参见 https://dev59.com/eVwZ5IYBdhLWcg3wTOzr),因此属性所发生的事情并不简单。 - Quentin
使用情况3,因为onclick需要一些JavaScript代码才能执行,而不是一个名称,所以会发生“处理程序”被解释为变量名的情况(就像在.js文件中作为单独一行时一样),这是未定义的,因此评估为false并且什么也不做。 - racraman
3个回答

5
是的,在内联处理程序中,属性文本基本上只是evaled,除了一些限制。什么?
<button onclick="handler">Click Me</button>

本质上等同于如果您有一个(真实的)处理程序引用了handler函数但未调用它,例如:

btn.onclick = () => {
  handler;
};

这不会抛出错误(因为handler是可引用的),但也不会运行handler,因为你只是引用该函数而没有调用它。

当你执行:

btn.onclick = handler;

你将一个对handler引用传递给onclick内部,然后当按钮被点击时,它会调用该引用。

(如果你使用了btn.onclick = handler;handler立即被调用,并且其返回值将被添加为监听器,这不是你想要的)

当你执行

<button onclick="handler(event);">Click Me</button>

您传递的event参数来自window.event,这就是为什么它可以被引用。
正如我之前所说,有一些限制:内联处理程序隐式地在运行的整个文本周围使用类似于with(this)的内容,其中this是相关元素。(正如您可以从Quentin's answer中看到的那样,它还使用with来处理document和包含的表单。) 例如:
<a onclick="search();">Click me!</div>

对于解释器来说,它看起来有点像:

<a onclick="
  with(this) {
    search();
  }
">Click me!</div>

这是有问题的,因为searchHTMLAnchorElement.prototype的属性。如果引用其他变量名,如果这些变量名在元素的原型链上任何位置都是属性,可能会出现类似的问题。

最好完全避免使用内联处理程序,并使用JavaScript正确附加事件。


是的,this确实很棘手,因为您可以触发目标元素原型链中的查找。但是您的回答证实了我的怀疑。我推测onclick="handler"之所以不起作用是因为它具有(有点)"eval"行为。因此,它只会有引用但不会“知道”调用引用的内容(在这种情况下是一个函数)。然后我想我的直觉让我走上了正确的轨道。在一些文档中也发现该参数期望"script",这并不意味着它期望一个函数作为参数,而实际上是期望一个字符串脚本以“eval”来说。 - Segers-Ian

2

这并不是实际发生的过程,但你可以这样想象:

假设JavaScript引擎将onclick属性的内容保存为字符串,当用户点击该元素时,再对其进行评估。

例如:

最初的回答

function foo(a, b) {
  alert(a + ' ' + b);
}
<button onclick="foo('hello', 'world')">click me</button>

<button onclick="(function () { alert ('I\'m an inline IIFE') })()">click me</button>

这与eval("foo('hello', 'world')")的结果相同。如果你传递eventthis或其他关键字呢?

那么,在这里,你可以想象一个JavaScript引擎执行以下操作:

最初的回答:

var this = /* create context */;
var event = /* create event */;
eval("foo(this, event)");

规范要求评估该属性!我并不是说引擎使用 eval :P

例子:

<button onclick="alert('hello world')">Click me</button>

btn.onclick = handler;呢?

这是另一种情况。 onclick属性是一个回调函数,需要一个函数引用。

最初的回答。


1
当您使用HTML属性来处理事件时,实际上在幕后创建了一个函数,该函数被分配为相关事件的处理程序。
  • 函数体是属性的内容。它应该是JavaScript代码。
  • 函数将event参数作为参数

因此,

在情况1中,生成的处理程序:

function handlerHtml(event) {
    handler()
}

在第二种情况下,生成的处理程序为:


function handlerHtml(event) {
    handler(event)
}

在第三种情况下,生成的处理程序为:
function handlerHtml(event) {
    handler
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接