【问题标题】:How to access the correct `this` inside a callback如何在回调中访问正确的`this`
【发布时间】:2026-01-03 05:55:02
【问题描述】:

我有一个注册事件处理程序的构造函数:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', function () {
        alert(this.data);
    });
}

// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};

// called as
var obj = new MyConstructor('foo', transport);

但是,我无法在回调中访问已创建对象的 data 属性。看起来this 不是指创建的对象,而是指另一个对象。

我也尝试使用对象方法而不是匿名函数:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', this.alert);
}

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

但它表现出同样的问题。

如何访问正确的对象?

【问题讨论】:

标签: javascript callback this


【解决方案1】:

你应该知道的关于this

this(又名“上下文”)是每个函数内部的一个特殊关键字,它的值仅取决于 如何 函数被调用,而不是如何/何时/在何处定义它。它不像其他变量那样受词法范围的影响(箭头函数除外,见下文)。以下是一些示例:

function foo() {
    console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

要了解有关this 的更多信息,请查看MDN documentation


如何引用正确的this

使用arrow functions

ECMAScript 6 引入了 箭头函数,可以将其视为 lambda 函数。他们没有自己的this 绑定。相反,this 就像普通变量一样在范围内查找。这意味着您不必致电.bind。这不是他们唯一的特殊行为,请参阅 MDN 文档了解更多信息。

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

不要使用this

您实际上并不想特别访问this,而是它引用的对象。这就是为什么一个简单的解决方案是简单地创建一个也引用该对象的新变量。变量可以有任何名称,但常见的是selfthat

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

由于self 是一个普通变量,它遵循词法范围规则并且可以在回调内部访问。这还有一个好处是您可以访问回调本身的this 值。

显式设置回调的this - 第 1 部分

您可能无法控制this 的值,因为它的值是自动设置的,但实际上并非如此。

每个函数都有.bind [docs] 方法,它返回一个新函数,this 绑定到一个值。该函数的行为与您调用.bind 的行为完全相同,只是this 是您设置的。无论以何种方式或何时调用该函数,this 将始终引用传递的值。

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

在这种情况下,我们将回调的this 绑定到MyConstructorthis 的值。

注意:当为 jQuery 绑定上下文时,请改用jQuery.proxy [docs]。这样做的原因是您在取消绑定事件回调时不需要存储对函数的引用。 jQuery 在内部处理。

设置this 的回调 - 第 2 部分

一些接受回调的函数/方法也接受回调的this 应该引用的值。这与自己绑定它基本上相同,但函数/方法会为您完成。 Array#map [docs]就是这样一种方法。它的签名是:

array.map(callback[, thisArg])

第一个参数是回调,第二个参数是this 应该引用的值。这是一个人为的例子:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

注意:您是否可以为this 传递值通常会在该函数/方法的文档中提及。例如,jQuery's $.ajax method [docs] 描述了一个名为 context 的选项:

这个对象将成为所有 Ajax 相关回调的上下文。


常见问题:使用对象方法作为回调/事件处理程序

此问题的另一个常见表现是对象方法用作回调/事件处理程序时。函数是 JavaScript 中的一等公民,术语“方法”只是作为对象属性值的函数的通俗术语。但该函数没有指向其“包含”对象的特定链接。

考虑以下示例:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

函数this.method被指定为点击事件处理器,但如果document.body被点击,记录的值将是undefined,因为在事件处理器内部,this指的是document.body,而不是Foo 的实例。
正如开头已经提到的,this 所指的内容取决于函数是如何调用,而不是它是如何定义
如果代码如下所示,则可能更明显的是该函数没有对该对象的隐式引用:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

解决方法同上:如果可用,使用.bindthis显式绑定到特定值

document.body.onclick = this.method.bind(this);

或显式调用函数作为对象的“方法”,通过使用匿名函数作为回调/事件处理程序并将对象 (this) 分配给另一个变量:

var self = this;
document.body.onclick = function() {
    self.method();
};

或使用箭头函数:

document.body.onclick = () => this.method();

【讨论】:

  • 菲利克斯,我以前读过这个答案,但从未回复过。我越来越担心人们使用selfthat 来指代this。我有这种感觉是因为this 是一个在不同上下文中使用的重载变量;而self 通常对应本地实例,that 通常指另一个对象。我知道你没有设置这个规则,因为我已经看到它出现在许多其他地方,但这也是我开始使用_this 的原因,但不确定其他人的感受,除了非- 已产生的统一实践。
  • @FelixKling,它可以让你超级懒惰地使用 $(...).on('click', $.proxy(obj, 'function'))$(...).off('click', obj.function) 这样的代码。
  • @FelixKling 有时依赖Function.prototype.call ()Function.prototype.apply () 会很有用。特别是apply (),我获得了很多里程。我不太倾向于使用 bind () 可能只是出于习惯,尽管我知道(但不确定)使用 bind 可能比其他选项有轻微的开销优势。
【解决方案2】:

这里有几种在子上下文中访问父上下文的方法- H2>
  1. 您可以使用@987654321@() 函数。
  2. 将 context/this 的引用存储在另一个变量中(参见下面的示例)。
  3. 使用 ES6 Arrow 函数。
  4. 更改代码、功能设计和架构 - 为此,您应该在 JavaScript 中控制 design patterns

1.使用bind()函数

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', ( function () {
        alert(this.data);
    }).bind(this) );
}
// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};
// called as
var obj = new MyConstructor('foo', transport);

如果您使用的是 Underscore.js - http://underscorejs.org/#bind

transport.on('data', _.bind(function () {
    alert(this.data);
}, this));

2.将 context/this 的引用存储在另一个变量中

function MyConstructor(data, transport) {
  var self = this;
  this.data = data;
  transport.on('data', function() {
    alert(self.data);
  });
}

3.箭头函数

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}

【讨论】:

  • bind() 选项令人惊叹,它只是将这个对象的指针传递给另一个对象上的 this(:谢谢!
  • …父上下文在子上下文中”是一个误导性的短语,因为 this 不是父/子关系的一部分。它通常指的是调用方法的对象,但可以是 any 对象,或严格模式下的 any 值。 “Context”指的是一个执行上下文(其中 this 是许多参数之一),因为 ECMA-262 禁止它,所以它不能被引用。
【解决方案3】:

这都是调用方法的“神奇”语法:

object.property();

当您从对象中获取属性并一次性调用它时,该对象将成为该方法的上下文。如果您调用相同的方法,但在不同的步骤中,则上下文是全局范围(窗口):

var f = object.property;
f();

当您获得方法的引用时,它不再附加到对象上。它只是对普通函数的引用。当您将引用用作回调时,也会发生同样的情况:

this.saveNextLevelData(this.setAll);

这就是你将上下文绑定到函数的地方:

this.saveNextLevelData(this.setAll.bind(this));

如果您使用的是 jQuery,则应该使用 $.proxy 方法,因为并非所有浏览器都支持 bind

this.saveNextLevelData($.proxy(this.setAll, this));

【讨论】:

    【解决方案4】:

    应该 知道“this”关键字。

    根据我的观点,您可以通过三种方式实现“this” (自/箭头函数/绑定方法)

    与其他语言相比,函数的 this 关键字在 JavaScript 中的行为略有不同。

    严格模式和非严格模式也有一些区别。

    在大多数情况下,this 的值取决于函数的调用方式。

    执行时不能通过赋值设置,每次调用函数时可能不同。

    ES5 引入了 bind() 方法来设置函数的 this 的值,不管它是如何被调用的,

    并且 ES2015 引入了不提供自己的 this 绑定的箭头函数(它保留了封闭词法上下文的这个值)。

    方法 1: Self - Self 被用来保持对原始 this 的引用,即使上下文发生变化。这是事件处理程序中经常使用的一种技术(尤其是在闭包中)。

    参考this

    function MyConstructor(data, transport) {
        this.data = data;
        var self = this;
        transport.on('data', function () {
            alert(self.data);
        });
    }
    

    方法 2:箭头函数 - 箭头函数表达式是正则函数表达式的语法紧凑替代方案,尽管它自己没有绑定到 this、arguments、super 或 new.target 关键字。

    箭头函数表达式不适合用作方法,不能用作构造函数。

    参考Arrow function expressions

      function MyConstructor(data, transport) {
        this.data = data;
        transport.on('data',()=> {
            alert(this.data);
        });
    }
    

    方法 3:Bind - bind() 方法创建一个新函数,在调用该函数时,将其 this 关键字设置为提供的值,并在任何提供的参数之前设置给定的参数序列新函数被调用。

    参考: Function.prototype.bind()

      function MyConstructor(data, transport) {
        this.data = data;
        transport.on('data',(function() {
            alert(this.data);
        }).bind(this);
    

    【讨论】:

      【解决方案5】:

      “上下文”的问题

      术语“上下文”有时用于指代 this 引用的对象。它的使用是不恰当的,因为它在语义上或技术上都不适合 ECMAScript's this

      "Context" 表示围绕某事增加意义的情况,或一些给予额外意义的前后信息。在 ECMAScript 中使用术语“上下文”来指代execution context,即在某些执行代码的范围内的所有参数、范围和 this

      这显示在ECMA-262 section 10.4.2:

      将 ThisBinding 设置为与 调用执行上下文

      这清楚地表明 this 是执行上下文的一部分。

      执行上下文提供了为正在执行的代码增加意义的周围信息。它包含的信息远不止thisBinding

      this 的值不是“上下文”。它只是执行上下文的一部分。它本质上是一个局部变量,可以通过调用任何对象并在严格模式下设置为任何值。

      【讨论】:

      • 不能同意这个答案。 “执行上下文”一词的存在并不禁止“上下文”的其他用途,就像它禁止“执行”的其他用途一样。也许有一个更好的术语来描述this,但这里没有提供任何内容,而且可以说现在关闭“上下文”的大门为时已晚。
      • @Roamer-1888——感谢您的编辑。你是对的,但我的论点并不依赖于“执行上下文”的存在,而排除了“上下文”用于其他目的。相反,它基于从技术和语义角度来看都不合适的“上下文”。我也认为使用“context”而不是“this”正在消失。我看不出有什么理由要找到 thisthisBinding 的替代术语,它只是混淆并意味着在某些时候你必须解释“上下文”实际上是 this,而且它无论如何都不是“上下文”。 :-)
      • 当您已经承认它是执行上下文的一部分时,我认为您不能说 this 不是“上下文”,其中“执行”只是形容词。
      • @Roamer-1888——我不会在这之后继续这个对话。是的,this 是执行上下文的part。说它是 the 上下文就像说一个团队中的一个玩家就是团队。
      【解决方案6】:

      首先,您需要清楚了解scope以及this关键字在scope上下文中的行为强>。

      this & scope:


      JavaScript 中有两种类型的作用域。它们是:

      1. 全球范围

      2. 功能范围

      简而言之,全局作用域是指窗口对象。在全局范围内声明的变量可以从任何地方访问。

      另一方面,函数作用域位于函数内部。函数内部声明的变量不能被外界正常访问。

      全局范围内的this关键字指的是窗口对象。 this 在函数内部也指的是窗口对象。所以 this 将始终引用窗口,直到我们找到一种方法来操纵 this 以指示我们自己选择的上下文。

      --------------------------------------------------------------------------------
      -                                                                              -
      -   Global Scope                                                               -
      -   (globally "this" refers to window object)                                  -
      -                                                                              -
      -   function outer_function(callback){                                         -
      -                                                                              -
      -       // Outer function scope                                                -
      -       // Inside the outer function, the "this" keyword                       -
      -       //  refers to window object                                            -
      -       callback() // "this" inside callback also refers to the  window object -
      -   }                                                                          -
      -                                                                              -
      -   function callback_function(){                                              -
      -                                                                              -
      -       // Function to be passed as callback                                   -
      -                                                                              -
      -       // Here "THIS" refers to the window object also                        -
      -   }                                                                          -
      -                                                                              -
      -   outer_function(callback_function)                                          -
      -   // Invoke with callback                                                    -
      -                                                                              -
      --------------------------------------------------------------------------------
      

      在回调函数中操作this 的不同方法:

      这里我有一个名为 Person 的构造函数。它有一个名为name的属性和四个名为sayNameVersion1sayNameVersion2sayNameVersion3的方法> 和 sayNameVersion4。他们四个人都有一个特定的任务。接受回调并调用它。回调有一个特定的任务是记录 Person 构造函数实例的 name 属性。

      function Person(name){
      
          this.name = name
      
          this.sayNameVersion1 = function(callback){
              callback.bind(this)()
          }
          this.sayNameVersion2 = function(callback){
              callback()
          }
      
          this.sayNameVersion3 = function(callback){
              callback.call(this)
          }
      
          this.sayNameVersion4 = function(callback){
              callback.apply(this)
          }
      
      }
      
      function niceCallback(){
      
          // Function to be used as callback
      
          var parentObject = this
      
          console.log(parentObject)
      }
      

      现在让我们从 person 构造函数创建一个实例,并使用 niceCallback 调用不同版本的 sayNameVersionX(X 指 1,2,3,4)方法看看我们有多少种方法可以操纵 this 内部回调来引用 person 实例。

      var p1 = new Person('zami') // Create an instance of Person constructor
      

      bind:

      bind 的作用是创建一个新函数,并将 this 关键字设置为提供的值。

      sayNameVersion1sayNameVersion2使用bind来操作回调函数的this

      this.sayNameVersion1 = function(callback){
          callback.bind(this)()
      }
      this.sayNameVersion2 = function(callback){
          callback()
      }
      

      第一个将 this 与方法本身内部的回调绑定。而对于第二个,回调是与绑定到它的对象一起传递的。

      p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
      
      p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
      

      call:

      call方法的first argument调用的函数中用作this call 附在上面。

      sayNameVersion3 使用 call 来操纵 this 来引用我们创建的 person 对象,而不是 window 对象。

      this.sayNameVersion3 = function(callback){
          callback.call(this)
      }
      

      它的调用如下:

      p1.sayNameVersion3(niceCallback)
      

      apply:

      call类似,apply的第一个参数指的是this将指示的对象关键字。

      sayNameVersion4 使用 apply 操纵 this 来引用人员对象

      this.sayNameVersion4 = function(callback){
          callback.apply(this)
      }
      

      它被称为如下。只需传递回调,

      p1.sayNameVersion4(niceCallback)
      

      【讨论】:

      • 对答案的任何建设性批评将不胜感激!
      • 全局范围内的 this 关键字不一定指向 window 对象。这仅在浏览器中是正确的。
      • @RandallFlagg 我从浏览器的角度写了这个答案。如有必要,请随意改进这个答案:)
      【解决方案7】:

      我们不能将它绑定到setTimeout(),因为它总是使用全局对象(Window)执行。如果你想在回调函数中访问this的上下文,那么通过在回调函数中使用bind(),我们可以这样实现:

      setTimeout(function(){
          this.methodName();
      }.bind(this), 2000);
      

      【讨论】:

      • 回复“Window”:不是“window”(小写)吗?
      【解决方案8】:

      问题围绕着 this 关键字在 JavaScript 中的行为。 this 的行为如下所示,

      1. this 的值通常由函数执行上下文确定。
      2. 在全局范围内,this 指的是全局对象(window 对象)。
      3. 如果为任何函数启用了严格模式,则this 的值将是undefined,因为在严格模式下,全局对象引用undefined 代替window 对象。
      4. 点之前的对象是this 关键字将绑定到的对象。
      5. 我们可以用call()bind()apply()显式设置这个值
      6. 当使用 new 关键字(构造函数)时, this 绑定到正在创建的新对象。
      7. 箭头函数不绑定this — 相反,this 是按词法绑定的(即,基于原始上下文)

      正如大多数答案所暗示的,我们可以使用箭头函数或bind() 方法或Self var。我会引用Google JavaScript Style Guide中关于lambdas(箭头函数)的一点

      比起 f.bind(this) 更喜欢使用箭头函数,尤其是 goog.bind(f, 这个)。避免写 const self = this。箭头函数 对于有时会意外通过的回调特别有用 附加参数。

      Google 明确建议使用 lambdas 而不是 bind 或 const self = this

      所以最好的解决方案是使用如下的 lambdas,

      function MyConstructor(data, transport) {
        this.data = data;
        transport.on('data', () => {
          alert(this.data);
        });
      }
      

      参考资料:

      1. https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
      2. arrow-functions-vs-bind

      【讨论】:

      【解决方案9】:

      如果在代码中使用类,目前还有另一种方法。

      class fields的支持下,可以通过以下方式实现:

      class someView {
          onSomeInputKeyUp = (event) => {
              console.log(this); // This refers to the correct value
          // ....
          someInitMethod() {
              //...
              someInput.addEventListener('input', this.onSomeInputKeyUp)
      

      可以肯定的是,在底层,绑定上下文的都是旧的好箭头函数,但在这种形式下,它看起来比显式绑定要清晰得多。

      由于这是第 3 阶段提案,您需要 Babel 和适当的 Babel plugin 来处理它,就像现在 (08/2018) 一样。

      【讨论】:

      • 这正是我在 Typescript 中工作的方式:public methodName = (params) =&gt; { body } 在类中。
      【解决方案10】:

      另一种方法,它是自 DOM2 以来的标准方法,在事件监听器中绑定 this让您始终移除监听器(以及其他好处),是来自EventListener 接口的handleEvent(evt) 方法:

      var obj = {
        handleEvent(e) {
          // always true
          console.log(this === obj);
        }
      };
      
      document.body.addEventListener('click', obj);
      

      关于使用handleEvent的详细信息可以在这里找到:DOM handleEvent: a cross-platform standard since year 2000

      【讨论】:

        【解决方案11】:

        我遇到了 Ngx 折线图 xAxisTickFormatting 函数的问题,该函数从 HTML 中调用,如下所示:[xAxisTickFormatting]="xFormat"

        我无法从声明的函数中访问我的组件变量。此解决方案帮助我解决了问题以找到正确的问题。

        而不是像这样使用函数:

        xFormat (value): string {
          return value.toString() + this.oneComponentVariable; //gives wrong result
        }
        

        使用这个:

         xFormat = (value) => {
           // console.log(this);
           // now you have access to your component variables
           return value + this.oneComponentVariable
         }
        

        【讨论】:

          【解决方案12】:

          其他人已经谈到了如何使用 .bind() 方法,但如果有人在让它们一起工作时遇到困难,那么具体来说,如果有人无法将它们与 .then() 一起使用,那么具体如下:

          someFunction()
          .then(function(response) {
              //'this' wasn't accessible here before but now it is
          }.bind(this))
          

          如 cmets 中所述,另一种方法是使用没有自己的“this”值的箭头函数

          someFunction()
          .then((response)=>{
              //'this' was always accessible here
          })
          

          【讨论】:

          • 这不正确。 (1) 箭头函数没有自己的this 值并使用关闭this-providing 环境中的值。 (2) 因此,.bind 对箭头函数没有影响。
          • 好电话,我复制代码错误,更新以显示两种变体
          【解决方案13】:

          this 在 JavaScript 中:

          JavaScript 中this 的值 100% 取决于函数的调用方式,而不是函数的定义方式。我们可以通过'点规则的左边'比较容易地找到this的值:

          1. 使用 function 关键字创建函数时,this 的值是被调用函数的点左侧的对象
          2. 如果点的左侧没有对象,则函数内的this 的值通常是全局对象(Node.js 中的global 和浏览器中的window)。我不建议在这里使用this 关键字,因为它比使用window 之类的关键字更不明确!
          3. 存在某些构造,例如箭头函数和使用Function.prototype.bind() 创建的函数,该函数可以修复this 的值。这些是规则的例外,但它们确实有助于修复 this 的值。

          Node.js 中的示例

          module.exports.data = 'module data';
          // This outside a function in node refers to module.exports object
          console.log(this);
          
          const obj1 = {
              data: "obj1 data",
              met1: function () {
                  console.log(this.data);
              },
              met2: () => {
                  console.log(this.data);
              },
          };
          
          const obj2 = {
              data: "obj2 data",
              test1: function () {
                  console.log(this.data);
              },
              test2: function () {
                  console.log(this.data);
              }.bind(obj1),
              test3: obj1.met1,
              test4: obj1.met2,
          };
          
          obj2.test1();
          obj2.test2();
          obj2.test3();
          obj2.test4();
          obj1.met1.call(obj2);
          

          输出:

          让我逐个介绍输出(忽略从第二个开始的第一个日志):

          1. thisobj2 因为点规则的左边,我们可以看到test1 是如何被称为obj2.test1(); 的。 obj2 位于点的左侧,因此是 this 值。
          2. 即使obj2 位于点的左侧,test2 通过bind() 方法绑定到obj1this 的值为 obj1
          3. obj2 位于被调用函数的点左侧:obj2.test3()。因此obj2 将是this 的值。
          4. 在这种情况下:obj2.test4() obj2 位于点的左侧。但是,箭头函数没有自己的this 绑定。因此它将绑定到外部作用域的this 值,即module.exports 一个在开始时记录的对象。
          5. 我们也可以使用call函数指定this的值。在这里,我们可以传入所需的 this 值作为参数,在本例中为 obj2

          【讨论】: