【问题标题】:How does JavaScript assign context to this of event handlers?JavaScript 如何为事件处理程序的 this 分配上下文?
【发布时间】:2026-02-06 22:10:01
【问题描述】:

看完相关问题#1,#2 我仍然没有找到以下问题的答案:

Javascript 可以设置上下文(即设置 this):bindcallapply

但是当我写一个事件处理程序时:

document.getElementById('myInput').onclick = function ()
                                                   {
                                                      alert(this.value)
                                                   }

谁/什么实际上附加thisobject本身?

附:使用 jQuery 时:

  $("#myInput").bind(function (){...})

bindcallapply)的内部实现

那么当我使用 jQuery 时,谁在做呢?

【问题讨论】:

  • DOM 实现负责该 IMO
  • @wroniasty 你能引用我吗?哪里有关于 context&this 的内容?在表格中,上下文讨论了里面的属性......
  • @wroniasty,你把我的话从我嘴里说出来了,这基本上是另一个“按定义”回答
  • 您的#1 和#2 都链接到同一个问题。

标签: javascript jquery


【解决方案1】:

当然,DOM/JavaScript 应该由 W3C 以这种方式工作。

事件处理程序在特定对象(当前事件目标)的上下文中调用,并随事件对象本身提供。

Source

这究竟是如何发生的,我们不知道。这是一个实现细节。

我们所知道的是,W3C 定义的语义是以某种方式实现的,但是浏览器的哪个部分以及如何做到这一点,这取决于浏览器开发人员,他们可以按照他们认为合适的方式实施。

【讨论】:

  • 我们知道这是怎么发生的。这就是 JavaScript 的工作原理。
  • @MarcusEkwall [误读,请勿阅读]:不,我们没有。我们知道发生了什么,而不是如何。这是有区别的。
  • 其实我们并不知道DOM implm是如何调用handler的。我们也不应该关心它。
  • @phant0m 对不起,我读到好像我们不知道 JavaScript 如何处理上下文,而不是 DOM 实现是如何工作的。我的坏:)
  • 事件处理程序是在特定对象的上下文中调用的,但常规函数(在其中使用[this])也是如此,所以有什么区别?
【解决方案2】:

总结所有讨论:

  • 一般来说,在调用o.x() 时,在函数调用中将this 绑定到o 的是JavaScript。
  • 但是,有一些调用函数的替代方法(如f.apply()f.call())可以改变这种行为。
  • onclick 是一个特例,调用它的方法是未知的,依赖于 DOM 实现。

【讨论】:

  • 为什么 onclick 是特殊情况?它只是一种方法...请纠正我
  • 这是一种特殊情况,因为它是由 DOM 调用的。它的上下文可以通过许多方法(element.call.onclick()element.onclick() 等)绑定到它的调用,并且它可能因 DOM 实现而异。
  • 所以仅仅因为同一个 Onclick 可以被不同的对象调用(没有使用 apply/bind/call)- 它是不同的?
  • 不,不是不同的对象。在某些时候,浏览器会收到通知,鼠标已在某个位置单击。然后它以某种方式评估哪个元素被“击中”。然后,它以某种方式对其 onclick 处理程序(如果存在)进行调用。然而,这叫什么方式,我们不得而知。不知道 JavaScript 解释器是否有效地以 element.onclick() 的方式获取代码来执行,或者浏览器是否有其他更优化的特殊情况来执行此操作。
  • @RoyiNamir 问题是,没有人可以肯定地回答这个问题,除非他编写了浏览器的那部分。但是因为不只是一个浏览器,你可能会得到多个非常不同但都是正确的答案。这就是为什么我和我想强调,这个问题没有具体的答案,而是接受它按照规范以某种未知的方式发生。
【解决方案3】:

说它是 DOM 的答案是错误的。

这是 JavaScript 本身的一部分,作为一种语言。 DOM 只是名称所指的“文档对象模型”,这正是 HTML 是如何通过使用 JavaScript 来表示以进行操作的。与 DOM 相关的对象遵循标准规定的行为,但这是通过使用 JS 来实现的。它是 JS 引擎,它与正在使用的任何布局引擎(Gecko、Trident、WebKit、Presto 等)进行通信。因此,如果 WebKit 检测到一个事件,它会按照 DOM 规范的指示将其传递给 JS 引擎,以便 JS 程序员可以操作它(这就是为什么你甚至会问这个问题,因为你可以使用它)。

换句话说,如果你在用 JavaScript 写东西,唯一能理解如何读取和执行的引擎就是 JS 引擎。该引擎(v8,SpiderMonkey/Jugger/Trace)将从布局引擎接收数据并使用它,以便您可以与之交互。同样,另一方面,每当您运行影响布局的代码时,布局引擎都会检测到更改,并会更改布局以便用户感知更改:即使 JS 代码可能已经启动了这一点,它是负责布局的布局引擎。

当您将函数分配给对象时,“this”是指函数所属的位置。因此,如果您将函数分配给对象 a 的实例,那么只要您在其中使用“this”,该函数就会引用 a。

如果您想从实现的角度来考虑它,请这样想: 每当你调用一个方法时,你首先要告诉一个实例你想调用一个带有 N 个参数的方法。此实例调用该方法,但将其自身添加到上下文中,如“this”。

在 Python 中,这通过将所有实例方法的第一个参数设为实例本身来更明确地完成。这里也是一样,只是实例是隐式传递的,而不是显式传递的。

请记住,实例拥有方法。当你执行“document.getElementById('something')”时,调用会返回一个对象(它恰好是一个 HTMLElement 对象,它是 DOM 的一部分,但这与 JS 与 DOM 交互的方式是巧合的),然后你正在分配作为属性点击的功能。

然后,每当您调用该方法时,JavaScript 引擎默认传递该实例,就像它传递其他变量一样(如参数也无需您执行任何操作即可生成,也由实现 ECMAScript 标准的 JS 引擎完成)。

我建议查看第 63 页:

“this 关键字计算为当前执行上下文的 ThisBinding 的值。”

但最重要的是,第 68 页“函数调用”

http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

【讨论】:

  • 这假设浏览器实际上评估 JavaScript 代码,例如:element.onclick(),这是一个没有根据的假设。浏览器没有需要这样做。
  • DOM 还负责处理点击事件,即调用处理程序。
  • 您使用的 DOM 是它的 JavaScript 实现。 “this”是什么,是 JavaScript 的问题。如果您愿意,您也可以在其他语言上使用 DOM,并且根据语言的不同,“this”可能甚至不存在。
  • 不,不是。它是 element.onclick()element.onclick.apply(context) 或等效的 DOM 实现。在第一种情况下,this 变为element,在后者中变为context
【解决方案4】:

在您的示例中,onclick 处理程序非常简单:DOM 元素 一个对象,您将 onclick 属性定义为一个函数。该函数实际上成为该 DOMElement/对象的方法。
当单击该对象时,该函数作为该元素的方法被调用,因此 this 指向它的 owner 元素。

简单地说,函数执行的上下文与创建的上下文相同(再次:在您的示例中作为 DOM 元素的方法),除非对函数的引用 object 被分配给另一个对象,或者当使用 callapply & co 在另一个上下文中调用该函数 object 时。当然:正如我在上面所暗示的,函数本身就是对象,并且被称为松散地耦合到它们的“所有者”。好吧,实际上他们没有这样的所有者,每次调用函数时,都会确定其上下文:

var foo = someObject.someFunction;//reference to method
someObject.someFunction();//this === someObject, context is the object preceding the function
foo();//implies [window].foo(); ==> this is window, except for strict mode

正如@wroniasty 指出的那样,我对所有权 的讨论可能有点令人困惑。问题是,函数是对象,它们不属于任何东西。当一个对象被分配一个方法时,该对象真正拥有的只是对给定函数对象的引用。当通过该引用调用该函数时,this 将指向拥有调用引用的对象。
当我们将其应用于您的elem.onclick = function(){} 时,我们看到元素仅拥有对在某个范围内声明的函数表达式的引用(全局、命名空间对象,无关紧要) .当 click 事件触发时,该 reference 将用于调用处理程序,从而将对该元素的引用分配给 this。澄清一下:

document.getElementById('foo').onclick = (function()
{//This function returns the actual handler
    var that = this;//closure var
    console.log(this);//logs window object
    //defined in global context
    return function(e)//actual handler
    {
        console.log(this === that);//false
        console.log(this);//elem
        console.log(that);//window
    };
})();//IIFE

所以处理程序是在全局上下文中声明的,并且处理程序可以访问它使用that 声明的上下文,这要归功于闭包(但这是另一回事)。关键是,事件使用元素fooonclick 属性引用处理程序。该属性是对函数对象的引用,因此函数对象将其上下文设置为进行调用的任何对象。

我确实希望这能消除我对函数的所有权造成的任何困惑,也许还有 JS 中的上下文是如何确定的。

【讨论】:

  • 匿名函数没有“所有者”
  • @wroniasty:this 不会指代窗口或文档或它正在运行的上下文吗?即使是匿名函数也必须在某些上下文中运行,不是吗?
  • @FrançoisWahl 是的,据我所知,每个函数都在某些上下文中运行。但是如果您使用术语“所有者”,那么一个函数可以有多个所有者。因此,“所有者”一词不是很好,因为它是一种临时关系,而不是功能固有的东西(Elias 解释得很好)。
  • @phant0m:我明白了。谢谢你解释这个。我自己还在学习 JavaScript、闭包和上下文等方面的优点。所以有时还是会有点困惑。
  • @wroniasty:在匿名的情况下。函数this 不会从其外部范围“继承” 上下文,如果这样做,您就不需要时不时地声明that_self var。每个函数都在全局上下文中调用,除非它被显式声明为方法并被调用为一个 OR 它已被绑定 (bind)、call-ed 或应用 (apply)明确地。就所有权而言:函数从来没有真正拥有所有者,这就是为什么我说它们绑定松散并且是独立的。首先从所有权的角度考虑可能会有所帮助。也许我应该澄清一下
【解决方案5】:

http://dmitrysoshnikov.com/ecmascript/chapter-3-this/#this-value-in-the-function-code

基本上,它是由 JavaScript 内部完成的。

上下文是调用函数的对象,例如

elem.onclick();  // elem === this

但是:

func = elem.onclick;
func() // global === this

【讨论】:

    【解决方案6】:

    这实际上与前面提到的 DOM 无关,而是 JavaScript 是如何设计为在对象中调用函数时工作的。

    以此为例:

    var myObject = {
        id: 1,
        onclick: null
    }
    
    myObject.onclick = function() {
        console.log(this.id);
    }
    

    调用myObject.onclick() 会将1 记录到控制台,这意味着myObject 是它的上下文。

    由于onclick 也是对象的属性,所以this 将是父对象,在您的情况下为HTMLElement

    【讨论】:

    • 谁告诉 JavaScript 在特定元素上发生了点击事件?
    • 浏览器中的 DOM 实现。然而,这正是他现在要问的。
    • 他问的是Who/What actually attaches this to the object,而不是javascript的工作原理。
    • 伙计们,福**我的问题,请不要打架,让我们找到正确的答案。坚持 JS 。 :-) 并感谢您尝试解决对 myne 的这种误解
    • 不!这已经太过分了,现在不能停止!只是在开玩笑。对不起@MarcusEkwall,我不是故意冒犯你的。
    【解决方案7】:

    出于说明目的,尽管实现可能不同,但请考虑以下函数

     function f() { alert(this.name); } 
    

    作为

    function f(this) { alert(this.name); } 
    

    this 想象成一个秘密参数,您可以使用 bind、apply 和 call 覆盖它,但通常会被浏览器设置为调用对象。

    例子

    var a = {},
        b = {};
    
    a.name = "John";
    b.name = "Tom";
    
    // "this" param added secretly
    function printName( ) { 
        console.log( this.name ) 
    };
    
    a.printName = printName     
    b.printName = printName;
    

    当调用 printName 函数时,浏览器会将“秘密”this 参数设置为调用函数。在下面的示例中,这是 b,因此“Tom”会打印到控制台。

    printName( ); // global context assumed so this === window
    b.printName( ); // this === b and outputs "Tom"
    printName.call( a ); //this === a and outputs "John"
    

    更多信息here

    【讨论】:

    • 函数 f(this) 是一个不错且令人耳目一新的网站,但是像 f(myObj) 这样的执行是在哪里进行的呢?