javascript tutorial - [Solved-5 Solutions] Access the correct ‘this’ inside a callback - javascript - java script - javascript array



Problem:

How to access the correct ‘this’ inside a callback?

Solution 1:

What we 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`
click below button to copy the code. By JavaScript tutorial team

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

How to refer to the correct this

Don't use this

We 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);
    });
}
click below button to copy the code. By JavaScript tutorial team
  • Since self is a normal variable, it obeys lexical scope rules and is accessible inside the callback. This also has the advantage that we can access the this value of the callback itself.
  • Explicitly set this of the callback - part 1
  • It might look like we 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 we called .bind on, only that thiswas 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);
}
click below button to copy the code. By JavaScript tutorial team

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

Note: When binding context for jQuery, use instead. The reason to do this is so that we 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 we 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));
}
click below button to copy the code. By JavaScript tutorial team
  • Set this of the callback - part 2
  • Some functions/methods which accept callbacks also accept a value to which the callback's thisshould 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])
click below button to copy the code. By JavaScript tutorial team

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
click below button to copy the code. By JavaScript tutorial team
  • Note: Whether or not we 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);
};
click below button to copy the code. By JavaScript tutorial team
  • 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;
click below button to copy the code. By JavaScript tutorial team

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);
click below button to copy the code. By JavaScript tutorial team

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();
};
click below button to copy the code. By JavaScript tutorial team

or use an arrow function:

document.body.onclick = () => this.method();
click below button to copy the code. By JavaScript tutorial team

Solution 2:

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

  • We can use bind() function -
  • Store reference to context/this inside another variable
  • Use ES6 Arrow functions -
  • Alter code/function architecture - for this we should have commands on design patterns in javascript -

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);
click below button to copy the code. By JavaScript tutorial team

If we are using underscore.js -

transport.on('data', _.bind(function () {
    alert(this.data);
}, this));
click below button to copy the code. By JavaScript tutorial team

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);
  });
}
click below button to copy the code. By JavaScript tutorial team

Arrow function

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
click below button to copy the code. By JavaScript tutorial team

Solution 3:

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

object.property();
click below button to copy the code. By JavaScript tutorial team

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

var f = object.property;
f();
click below button to copy the code. By JavaScript tutorial team

When we 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 we get the reference to use as a callback:

this.saveNextLevelData(this.setAll);
click below button to copy the code. By JavaScript tutorial team

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

this.saveNextLevelData(this.setAll.bind(this));
click below button to copy the code. By JavaScript tutorial team

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

this.saveNextLevelData($.proxy(this.setAll, this));
click below button to copy the code. By JavaScript tutorial team

Solution 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.

Solution 5:

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.

Related Searches to javascript tutorial - Access the correct ‘this’ inside a callback