DEV Community

krism4n5z
krism4n5z

Posted on

Passing this in callbacks in one example

I know that this subject could be confusing for even experienced developers but I want to explain it using my knowledge and experience and using my own words.

I will use word invocation and function calling interchangeable.

What is this?

I understand this as a context(object) for function in which this function is executed. Kyle Simpson in his book "You don't know JS" explains this more deeply, so if you want to know everything about this you should check his book. It's free available in github.
For this post what you should know is:

Execute function without any bind, call, apply method and without any object explicitly (like x.a()) always -> this always refers to global:

a(); 

No matter where you see this type of function call, this always refers to global object.

This in callback

function get(url, success) {
    let httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url);
    httpRequest.onload = success;
    httpRequest.send();
};

function successHandler() {
  console.log(this);
}

const apiKey = 'd126cacbbfebf7c84ad878e9deffc0e1';
const url = 'https://api.openweathermap.org/data/2.5/weather?q=los+angeles&APPID=' + apiKey;

 get(url, successHandler);

What is the output of console log?
XMLHttpRequest object
Why?
Because we assigned a whole function definition (imagine function definition as a chocolate box) to onload property in XMLHttpRequest object, so now our function is a method. When the data get back from request our method is executed and because the owner (object, context) of the methods is a XMLHttpRequest object this is binding to this object.
We know that our function is executed like this:

XMLHttpRequest.onload()

So we explicitly show the owner of the function (XMLHttpRequest object) and implicitly bind this to this object.

But what if we want to pass arguments to our function?
If we do something like that:

function get(url, success) {
    let httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url);
    httpRequest.onload = success(httpRequest.responseText);
    httpRequest.send();
};

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

const apiKey = 'd126cacbbfebf7c84ad878e9deffc0e1';
const url = 'https://api.openweathermap.org/data/2.5/weather?q=los+angeles&APPID=' + apiKey;

 get(url, successHandler);

We try to invoke

XMLHttpRequest.onload()

but now it is not a method call but call on outcome of the success(httpRequest.responseText) invocation with some unexpected result. We try to use parenthesis to call a method but except we have not calling a method but the outcome of success(httpRequest.responseText) invocation. This is similar behavior to do this:

let a;
a();

You have type error because you cannot invoke a variable.

So what we should do?

function get(url, success) {
    let httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url);
    httpRequest.onload = function() {
success(httpRequest.responseText);
}
    httpRequest.send();
};

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

const apiKey = 'd126cacbbfebf7c84ad878e9deffc0e1';
const url = 'https://api.openweathermap.org/data/2.5/weather?q=los+angeles&APPID=' + apiKey;

 get(url, successHandler);

Now we have onload as a method. When onload is invoked our success handler is invoked similar to:

a();

So its this context is global.

How to create this context to XMLHttpRequest:

We can use bind to set this and argument and immediately invoke.

function get(url, success) {
    let httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url);
    httpRequest.onload = function() {
     success.bind(this, httpRequest.responseText)();
    }
    httpRequest.send();
};

function successHandler(data) {
  console.log(this); //XMLHttpRequest
   console.log(data);
}

We can simply create new function and bind success function to this and assign to newly created variable

function get(url, success) {
    let httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url);
    httpRequest.onload = function() {
     let bindedSuccess = success.bind(this);
bindedSuccess(httpRequest.responseText);
    }
    httpRequest.send();
};

function successHandler(data) {
  console.log(this); //XMLHttpRequest
   console.log(data);
}

Use arrow function

function get(url) {
    let httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', url);
    httpRequest.onload = function() {
      ((data) => {console.log(this)
 console.log(data);})(httpRequest.responseText); 
    };
    httpRequest.send();
};



const apiKey = 'd126cacbbfebf7c84ad878e9deffc0e1';
const url = 'https://api.openweathermap.org/data/2.5/weather?q=los+angeles&APPID=' + apiKey;

 get(url);

Top comments (0)