javascript - 如何在回调中访问正确的“this”?

标签 javascript callback this

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

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

但也有同样的问题。
如何访问正确的对象?

最佳答案

关于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
不要使用this
实际上,您并不想特别访问this,而是要访问它所引用的对象。这就是为什么一个简单的解决方案是简单地创建一个新的变量,它也引用那个对象。变量可以有任何名称,但常见的名称是selfthat
function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

由于cc是一个正常变量,所以它遵循词法范围规则,并且可以在回调中访问。这还有一个优点,就是您可以访问回调本身的self值。
显式设置回调的this-第1部分
可能看起来您无法控制this的值,因为它的值是自动设置的,但事实并非如此。
每个函数都有this [docs]方法,该方法返回一个新函数,其中.bind绑定到一个值。该函数与您调用的this on的行为完全相同,只是由您设置了.bind。无论如何或何时调用该函数,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绑定到this'sMyConstructor的值。
注意:当为jquery绑定上下文时,请改用this [docs]。这样做的原因是,在取消绑定事件回调时不需要存储对函数的引用。jquery在内部处理这个问题。
ecmascript 6:使用arrow functions
ecmascript 6引入了箭头函数,可以将其看作lambda函数。它们没有自己的jQuery.proxy绑定。相反,在作用域中查找this就像在正常变量中查找一样。这意味着您不必调用this。这不是他们唯一的特殊行为,请参阅MDN文档以获取更多信息。
function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

设置回调的.bind-第2部分
一些接受回调的函数/方法也接受回调的this应该引用的值。这基本上和你自己绑定一样,但是函数/方法为你做了。this [docs]就是这样一种方法。其签名为:
array.map(callback[, thisArg])

第一个参数是回调,第二个参数是Array#map应该引用的值。下面是一个人为的例子:
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

注意:在函数/方法的文档中通常会提到是否可以传递一个值“cc>”。例如,jQuery's this method [docs]描述了一个名为this的选项:
此对象将成为所有与ajax相关的回调的上下文。
常见问题:使用对象方法作为回调/事件处理程序
此问题的另一种常见表现是当对象方法用作回调/事件处理程序时。函数在javascript中是一等公民,术语“方法”只是一个通俗的术语,指的是作为对象属性值的函数。但是该函数没有与它的“包含”对象的特定链接。
请考虑以下示例:
function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

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

函数$.ajax被指定为单击事件处理程序,但如果单击context,则记录的值将为this.method,因为在事件处理程序内部,document.body指的是undefined,而不是this的实例。
正如前面提到的,document.body指的是什么取决于函数是如何调用的,而不是如何定义的。
如果代码如下所示,则更明显的是函数没有对对象的隐式引用:
function method() {
    console.log(this.data);
}


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

Foo.prototype.method = method;

该解决方案与上面提到的相同:如果可用,使用Foothis显式绑定到特定值。
document.body.onclick = this.method.bind(this);

或者显式调用函数作为对象的“方法”,方法是使用匿名函数作为回调/事件处理程序,并将对象(.bind)分配给另一个变量:
var self = this;
document.body.onclick = function() {
    self.method();
};

或使用箭头功能:
document.body.onclick = () => this.method();

关于javascript - 如何在回调中访问正确的“this”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53691094/

相关文章:

javascript - 从 View 中重新排序 Backbone 集合

javascript - jQuery 向/从多个元素添加和删除类

javascript - 在 if 语句上使用 Promises

c++ - 泛化 C++11 线程类以使用 lambda

javascript - 如何在 jQuery 触发器 ("click"上运行回调函数?

c++ - 使用指向非静态成员函数的函数指针时,可以省略 "this"关键字吗

javascript - 循环内 Lodash Throttle 或 Debounce 的问题

javascript - Extjs + PHP 通过增加 Java 堆大小来增强性能

javascript - 使用 JSON 定义的函数时是否可以使用相对语法访问 JSON 属性?

Javascript Function.prototype.call() 传递原始值