首页  >  问答  >  正文

如何在回调函数中正确访问`this`对象

<p>我有一个构造函数,它注册了一个事件处理程序:</p> <p><br /></p> <pre class="snippet-code-js lang-js prettyprint-override"><code>function MyConstructor(data, transport) { this.data = data; transport.on('data', function () { alert(this.data); }); } // 模拟传输对象 var transport = { on: function(event, callback) { setTimeout(callback, 1000); } }; // 调用方式 var obj = new MyConstructor('foo', transport);</code></pre> <p><br /></p> <p>然而,在回调函数中我无法访问创建的对象的<code>data</code>属性。看起来<code>this</code>并不指向被创建的对象,而是指向另一个对象。</p> <p>我还尝试使用对象方法而不是匿名函数:</p> <pre class="brush:php;toolbar:false;">function MyConstructor(data, transport) { this.data = data; transport.on('data', this.alert); } MyConstructor.prototype.alert = function() { alert(this.name); };</pre> <p>但是它也出现了相同的问题。</p> <p>如何访问正确的对象?</p>
P粉608647033P粉608647033449 天前535

全部回复(2)我来回复

  • P粉921130067

    P粉9211300672023-08-21 14:13:52

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

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

    1. 使用bind()函数

    function MyConstructor(data, transport) {
        this.data = data;
        transport.on('data', ( function () {
            alert(this.data);
        }).bind(this) );
    }
    // 模拟传输对象
    var transport = {
        on: function(event, callback) {
            setTimeout(callback, 1000);
        }
    };
    // 调用方式
    var obj = new MyConstructor('foo', transport);
    

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

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

    2. 将上下文/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);
      });
    }
    

    回复
    0
  • P粉176151589

    P粉1761515892023-08-21 10:04:21

    关于 this 的知识

    this(也称为“上下文”)是每个函数内部的特殊关键字,其值仅取决于函数的调用方式,而不是定义方式。它不受词法作用域的影响,与其他变量不同(除了箭头函数,见下文)。以下是一些示例:

    function foo() {
        console.log(this);
    }
    
    // 普通函数调用
    foo(); // `this` 将引用 `window`
    
    // 作为对象方法
    var obj = {bar: foo};
    obj.bar(); // `this` 将引用 `obj`
    
    // 作为构造函数
    new foo(); // `this` 将引用从 `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,而是需要访问它所指的对象。因此,一个简单的解决方案是创建一个新变量,也指向该对象。变量可以有任何名称,但常见的是selfthat

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

    由于self是一个普通变量,它遵循词法作用域规则,并且可以在回调函数内部访问。这还有一个优点,即可以访问回调函数本身的this值。

    显式设置回调函数的this - 第一部分

    看起来你无法控制this的值,因为它的值是自动设置的,但实际上并非如此。

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

    function MyConstructor(data, transport) {
        this.data = data;
        var boundFunction = (function() { // 括号不是必需的
            alert(this.data);             // 但可能提高可读性
        }).bind(this); // <- 这里我们调用了`.bind()`
        transport.on('data', boundFunction);
    }

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

    注意:对于jQuery的绑定上下文,使用jQuery.proxy [文档]。这样做的原因是在解绑事件回调时不需要存储对函数的引用。jQuery在内部处理这个。

    设置回调函数的this - 第二部分

    一些接受回调函数的函数/方法也接受一个值,该值应该作为回调函数的this。这与手动绑定相同,但是函数/方法会为你执行绑定。例如,Array#map [文档]就是这样的方法。它的签名是:

    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); // <- 这里我们将`obj`作为第二个参数传递

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


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

    这个问题的另一个常见表现形式是将对象方法用作回调/事件处理程序。在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);

    或者通过使用匿名函数作为回调/事件

    回复
    0
  • 取消回复