8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

如何在回调中访问正确的 this

user3932000 2月前

185 0

我有一个注册事件处理程序的构造函数: function MyConstructor(data, transport) { this.data = data; transport.on('data', function () { alert(this.data);...

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

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);
};

但它也存在同样的问题。

我如何才能访问正确的对象?

帖子版权声明 1、本帖标题:如何在回调中访问正确的 this
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由user3932000在本站《jquery》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 您应该了解的内容 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 文档 .


    如何引用正确的 this

    使用 箭头函数

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

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

    不要使用 this

    实际上,您并不想访问 this 特定的对象,而是 访问它所引用的对象 。因此,一个简单的解决方案就是创建一个也引用该对象的新变量。变量可以有任何名称,但常见的名称是 self that .

    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 MyConstructor this .

    注意: 当绑定 jQuery 上下文时,请改用 jQuery.proxy [docs] 。这样做的原因是,在解除事件回调绑定时,您无需存储对函数的引用。jQuery 会在内部处理该问题。

    回调函数 this 设置

    一些接受回调的函数/方法也接受回调 this 应该引用的值。这基本上与自己绑定相同,但函数/方法会为您完成绑定。Array 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 单击了,则记录的值将是 un定义 ,因为在事件处理程序内部, this 指的是 document.body ,而不是的实例 Foo .
    正如开头提到的, this 引用取决于函数如何被 调用 ,而不是如何 defined .
    如果代码如下所示,则可能更明显的是该函数没有对该对象的隐式引用:

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

    解决方案 与上面提到的相同:如果可用,使用 .bind 显式绑定 this 到特定值

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

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

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

    或者使用箭头函数:

    document.body.onclick = () => this.method();
    
  • Felix,我之前读过这个答案,但从未回复过。我越来越担心人们使用 self 和 that 来指代 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 可能比其他选项略有开销优势。

  • 需要注意的是,bind() 将在解释过程中对第一次遇到的上下文进行快照...也就是说,当 JavaScript 第一次到达 bind() 函数时,它将获取当时的上下文!这可能很难排除故障,因为 bind() 的标准实现无法更改。一旦函数绑定到另一个对象,它将保持与该对象的绑定,尝试重新绑定它将不起作用。

  • 以下是在子上下文中访问父上下文的几种方法 -

    1. 您可以使用该 bind () 功能。
    2. 将对 context/this 的引用存储在另一个变量中(参见下面的示例)。
    3. 使用 ES6 箭头 函数。
    4. 改变代码、功能设计和架构——为此,您应该掌握 JavaScript 中的 设计模式

    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);
      });
    }
    
  • \'…子上下文中的父上下文\' 是一个误导性短语,因为这不是父子关系的一部分。它通常指的是调用方法的对象,但在严格模式下可以是任何对象或任何值。\'Context\' 指的是执行上下文(这是众多参数中的一个),由于 ECMA-262 禁止,因此无法引用。

  • 这一切都在于调用方法的“神奇”语法:

    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));
    
  • 引用 10

    应该 了解“this”关键字。

    按照我的观点,你可以通过三种方式实现 \'this\' (Self|Arrow function|Bind Method)

    ,函数 this 关键字在 JavaScript 中的行为略有不同。

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

    大多数情况下,this 的值由函数的调用方式决定。

    它不能在执行过程中通过赋值来设置,并且每次调用函数时它可能都不同。

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

    ES2015 引入了不提供自身 this 绑定的箭头函数(它保留了封闭词法上下文的 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 关键字的绑定。

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

    参考 箭头函数表达式

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

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

    参考: Function.prototype.bind()

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

    “背景”的问题

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

    “上下文” 是指围绕某事物的环境,这些环境赋予了它意义,或者一些前后信息赋予了它额外的意义。ECMAScript 中使用的术语“上下文”是指 执行上下文 某些执行代码范围内的 所有参数、作用域和 this

    ECMA-262 第 10.4.2 节 中显示了这一点 :

    将 ThisBinding 设置为与调用执行上下文的 ThisBinding 相同的值

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

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

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

  • 不能同意这个答案。术语“执行上下文”的存在并不禁止“上下文”的其他用途,就像它不禁止“执行”的其他用途一样。也许有更好的术语来描述这一点,但这里没有提供,而且可以说关闭“上下文”的大门已经太晚了。

  • @Roamer-1888 — 感谢您的编辑。您说得对,但我的论点并不依赖于“执行上下文”的存在,从而排除“上下文”用于其他目的。相反,它基于“上下文”从技术和语义角度来看都不合适。我还认为使用“上下文”代替“这个”的做法正在消亡。我看不出有任何理由去寻找 this 或 thisBinding 的替代术语,它只是混淆,意味着在某个时候你必须解释“上下文”实际上是这个,而它无论如何都不是“上下文”。:-)

  • 当您已经承认它是执行上下文的一部分时,我认为您不能说这根本不是“上下文”,其中“执行”仅仅是形容词。

  • 引用 15

    @Roamer-1888—我不会继续讨论这个问题。是的,这是执行上下文的一部分。说它是上下文就像说一个团队的一名球员就是整个团队。

  • 首先,你需要清楚地理解 作用域 作用域 上下文中 this 的行为 .

    这和范围:


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

    1. p3

    2. p4

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

    另一方面,函数作用域位于函数内部。函数内部声明的变量通常无法从外部访问。

    全局范围内的 函数 关键字指的是窗口对象。 将始终引用窗口,直到我们找到一种方法来操纵 内的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 和四个方法,分别称为 sayNameVersion1 , sayNameVersion2 , sayNameVersion3 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 构造函数创建一个实例,并 (X 代表 1,2,3,4)方法, sayNameVersionX niceCallback 看看我们可以通过多少种方式操纵 this 来引用 person 实例。

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

    绑定(bind):

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

    sayNameVersion1 sayNameVersion2 使用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
    

    称呼:

    参数一个 this call 的函数内部 用作 call

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

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

    它的调用方式如下:

    p1.sayNameVersion3(niceCallback)
    

    申请:

    类似 call 的第一个参数 apply 所指示的对象 this

    sayNameVersion4 使用 apply 来操纵 this 来引用 person 对象

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

    它的调用方式如下。只需传递回调即可,

    p1.sayNameVersion4(niceCallback)
    
  • 引用 17

    全局范围内的 this 关键字不一定引用 window 对象。只有在浏览器中才如此。

  • 引用 18

    @RandallFlagg 我是从浏览器的角度写了这个答案的。如有必要,可以随意增强这个答案:)

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

    setTimeout(function(){
        this.methodName();
    }.bind(this), 2000);
    
  • 回复 \'Window\':它不是 \'window\' (小写) 吗?

返回
作者最近主题: