找到你要的答案

Q:How to access the correct `this` inside a callback? 1. Use bind() function 2 Store reference to context/this inside another variable

Q:如何在回调中访问正确的“这个”? 1。()函数的使用 2商店参考上下文/这里面另一个变量

I have a constructor function which registers an event handler:

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

However, I'm not able to access the data property of the created object inside the callback. It looks like this does not refer to the object that was created but to an other one.

I also tried to use an object method instead of an anonymous function:

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

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

but it exhibits the same problems.

How can I access the correct object?

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

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

但是,我无法访问回调内创建对象的数据属性。看起来这并不是指创建的对象,而是指向另一个对象。

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

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

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

但它表现出同样的问题。

如何访问正确的对象?

answer1: 回答1:

What you should know about this

this (aka "the context") is a special keyword inside each function and its value only depends on how the function was called, not how/when/where it was defined. It is not affected by lexical scope, like other variables. Here are some examples:

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`

To learn more about this, have a look at the MDN documentation.


How to refer to the correct this

Don't use this

You actually don't want to access this in particular, but the object it refers to. That's why an easy solution is to simply create a new variable that also refers to that object. The variable can have any name, but common ones are self and that.

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

Since self is a normal variable, it obeys lexical scope rules and is accessible inside the callback. This also has the advantage that you can access the this value of the callback itself.

Explicitly set this of the callback - part 1

It might look like you have no control over the value of this, because its value is set automatically, but that is actually not the case.

Every function has the method .bind [docs], which returns a new function with this bound to a value. The function has exactly the same behavior as the one you called .bind on, only that this was set by you. No matter how or when that function is called, this will always refer to the passed value.

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

In this case, we are binding the callback's this to the value of MyConstructor's this.

Note: When binding context for jQuery, use jQuery.proxy [docs] instead. The reason to do this is so that you don't need to store the reference to the function when unbinding an event callback. jQuery handles that internally.

ECMAScript 6: Use arrow functions

ECMASCript 6 introduces arrow functions, which can be thought of as lambda functions. They don't have their own this binding. Instead, this is looked up in scope just like a normal variable. That means you don't have to call .bind. That's not the only special behavior they have, please refer to the MDN documentation for more information.

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

Set this of the callback - part 2

Some functions/methods which accept callbacks also accept a value to which the callback's this should refer to. This is basically the same as binding it yourself, but the function/method does it for you. Array#map [docs] is such a method. Its signature is:

array.map(callback[, thisArg])

The first argument is the callback and the second argument is the value this should refer to. Here is a contrived example:

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

Note: Whether or not you can pass a value for this is usually mentioned in the documentation of that function/method. For example, jQuery's $.ajax method [docs] describes an option called context:

This object will be made the context of all Ajax-related callbacks.


Common problem: Using object methods as callbacks / event handlers

Another common manifestation of this problem is when an object method is used as callback / event handler. Functions are first class citizens in JavaScript and the term "method" is just a colloquial term for a function that is a value of an object property. But that function doesn't have a specific link to its "containing" object.

Consider the following example:

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

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

The function this.method is assigned as click event handler, but if the body is clicked, the value logged will be undefined, because inside the event handler, this refers to the body, not the instance of Foo.
As already mentioned at the beginning, what this refers to depends on how the function is called, not how it is defined.
If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:

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


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

Foo.prototype.method = method;

The solution is the same as mentioned above: If available, use .bind to explicitly bind this to a specific value

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

or explicitly call the function as a "method" of the object, by using an anonymous function has callback / event handler and assign the object (this) to another variable:

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

or use an arrow function:

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

What you should know about 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`

了解更多关于这个,在MDN文档的外观。


How to refer to the correct this

Don't use this

你实际上不想访问这个,尤其是它所指的对象。这就是为什么一个简单的解决方案是简单地创建一个新的变量,也提到了对象。变量可以有任何名称,但常见的是自。

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

由于自是一个正态变量,它遵从词法范围规则,在回调函数中是可访问的。这也有好处,您可以访问此值的回调本身。

Explicitly set this of the callback - part 1

它看起来像你无法控制这个值,因为它的值是自动设置的,但事实并非如此。

每一个函数的方法。结合[文件],然后返回一个新的函数,这个值绑定。该函数具有与你所调用的完全相同的行为。绑定,只有这是由你设置的。无论如何或何时调用该函数,总是将其传递给所传递的值。

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

在这种情况下,我们结合回调的这MyConstructor的价值的。

注:当结合上下文使用jQuery,jquery.proxy [文件]代替。这样做的原因是,你不需要存储的参考函数时释放事件回调。jQuery处理内部。

ECMAScript 6: Use arrow functions

ECMAScript 6引入了箭头的函数,它可以被认为是lambda函数。他们没有自己的这种约束力。相反,这是在范围内,就像一个正常的变量。这意味着你不必打电话。这并不是他们唯一的特殊行为,请参阅MDN文档信息。

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

Set this of the callback - part 2

一些函数/方法接受回调也接受价值,回调是指。这基本上和你自己绑定一样,但是函数/方法为你做了。阵列#地图[文件]就是这样的一种方法。其签名是:

array.map(callback[, thisArg])

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

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

注:无论您是否可以传递一个值,这通常在该函数/方法的文档中提到。例如,jQuery的Ajax方法[文件]美元。描述了一个选项,称为上下文:

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


Common problem: Using object methods as callbacks / event handlers

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

考虑下面的例子:

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

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

The function this.method is assigned as click event handler, but if the body is clicked, the value logged will be undefined, because inside the event handler, this refers to the body, not the instance of Foo.
As already mentioned at the beginning, what this refers to depends on how the function is called, not how it is defined.
If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:

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


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

Foo.prototype.method = method;

该解决方案与上面提到的相同:如果有,请使用。绑定显式绑定到特定值。

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

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

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

或使用箭头函数:

document.body.onclick = () => this.method();
answer2: 回答2:

Here are several ways to access parent context inside child context -

  1. You can use bind() function - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
  2. Store reference to context/this inside another variable
  3. Alter code/function architecture - for this you should have commands on design patterns in javascript - https://addyosmani.com/resources/essentialjsdesignpatterns/book/

  4. Use ES6 Arrow functions - https://derickbailey.com/2015/09/28/do-es6-arrow-functions-really-solve-this-in-javascript/

1. Use bind() function

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

If you are using underscore.js - http://underscorejs.org/#bind

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

2 Store reference to context/this inside another variable

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

Here are several ways to access parent context inside child context -

  1. You can use bind() function - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
  2. Store reference to context/this inside another variable
  3. 改变代码/功能结构-这你应该命令对JavaScript设计模式https://addyosmani.com/resources/essentialjsdesignpatterns/book/

  4. 使用箭头https://derickbailey.com/2015/09/28/do-es6-arrow-functions-really-solve-this-in-javascript/ ES6功能

1. Use bind() function

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

如果您使用的是http:/ / underscorejs Underscore.js。org / #绑定

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

2商店参考上下文/这里面另一个变量

function MyConstructor(data, transport) {
  var self = this;
  this.data = data;
  transport.on('data', function() {
    alert(self.data);
  });
}
answer3: 回答3:

It's all in the "magic" syntax of calling a method:

object.property();

When you get the property from the object and call it in one go, the object will be the context for the method. If you call the same method, but in separate steps, the context is the global scope (window) instead:

var f = object.property;
f();

When you get the reference of a method, it's no longer attached to the object, it's just a reference to a plain function. The same happens when you get the reference to use as a callback:

this.saveNextLevelData(this.setAll);

That's where you would bind the context to the function:

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

If you are using jQuery you should use the $.proxy method instead, as bind is not supported in all browsers:

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

这都是在“魔术”的语法调用方法:

object.property();

当你从一个对象获取属性并调用它时,对象将是该方法的上下文。如果调用相同的方法,但在单独的步骤中,上下文是全局作用域(窗口),而不是:

var f = object.property;
f();

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

this.saveNextLevelData(this.setAll);

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

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

如果您使用的是jQuery应该使用美元。代理的方法相反,结合是不是所有的浏览器都支持:

this.saveNextLevelData($.proxy(this.setAll, this));
answer4: 回答4:

The trouble with "context"

The term "context" is sometimes used to refer to the object referenced by this. It's use is inappropriate because it doesn't fit either semantically or technically with ECMAScript's this.

"Context" means the circumstances surrounding something that adds meaning, or some preceding and following information that gives extra meaning. The term "context" is used in ECMAScript to refer to execution context, which is all the parameters, scope and this within the scope of some executing code.

This is shown in ECMA-262 section 10.4.2:

Set the ThisBinding to the same value as the ThisBinding of the calling execution context

which clearly indicates that this is part of an execution context.

An execution context provides the surrounding information that adds meaning to code that is being executed. It includes much more information that just the thisBinding.

So the value of this isn't "context", it's just one part of an execution context. It's essentially a local variable that can be set by the call to any object and in strict mode, to any value at all.

The trouble with "context"

术语“上下文”有时用于引用引用的对象。它的使用是不恰当的因为它不符合任何语义上或技术上与ECMAScript的这。

“语境”指的是周围事物的环境,它增加了意义,或一些前面和后面的信息赋予了额外的意义。“语境”是在ECMAScript用于指执行上下文,这是所有的参数范围内,这部分执行代码的范围。

这是在ECMA-262第10.4.2示:

Set the ThisBinding to the same value as the ThisBinding of the calling execution context

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

执行上下文提供对正在执行的代码添加意义的周围信息。它包含了更多的信息,只是thisbinding。

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

javascript  callback  this