Why has the context changed?

Asked

Viewed 83 times

3

I’m doing my Javascript studies, and I’m doing some pretty simple to consume an API, but I got an error. Before I comment on it, I’ll show you my code:

View js.

var model = new Model();
var response = null;
model.setUrl("https://jsonplaceholder.typicode.com/users");
model.openXhr("GET");

Model js.

var Model = function() {
    var _url = null;
    var _xhr = new XMLHttpRequest();
    var _message = {};
    var _response = null;

    this.setUrl = function (url) {
        _url = url;
    }

    this.getUrl = function (url) {
        return _url;
    }

    this.openXhr = function(method) {
        _xhr.open(method, this.getUrl());
        _xhr.onload = this.onLoadXhr;
        _xhr.send();
    }

    this.onLoadXhr = function() {
        if (_xhr.status === 200)
            _message.status = "success";
        else
            _message.status = "failed";

        var response = this.parseResponse(_xhr.responseText); // this.parseResponse is not a function

        this.setResponse(response);
    }

    this.parseResponse = function(response) {
        return JSON.parse(response);
    }

    this.setResponse = function(response) {
        _response = response;
    }

    this.getResponse = function() {
        return _response;
    }


}

The problem

var response = this.parseResponse(_xhr.responseText); // this.parseResponse is not a function

See that comes to this part it gives this error when calling the function. Printing the this I found that he is not from the context of the function, but from the _xhr. Why? In another part of the code I do a similar operation and it doesn’t happen _xhr.onload = this.onLoadXhr;. Here I referred to onLOadXhr within the openXhr.

3 answers

5


The problem is that the _xhr.onload will be run by the instance of Ajax and when you do _xhr.onload = this.onLoadXhr; you pass a function, with no associated context, so you will be given the this of the object to call.

Use like this: _xhr.onload = this.onLoadXhr.bind(this);, that way you pass your method, already forcing the context.

An example to clarify:

function Obj(fn) {
    this.teste = fn;
}

function foo(quem) {
    console.log(quem, this === window);
}

new Obj(foo).teste('dentro do obj');
foo('fora do objeto');

  • So whenever you pass a function as a reference it will not come with associated context ?

  • @Allanramos depends, in which case the function is called as XMLHttpRequest and hence the context change.

  • Because it is associated with the XHR object. If I didn’t have this object, how would it look ?

  • @Allanramos added an example to the answer

1

Normal in situations like this is to declare a variable to store a reference to the object itself. For example:

var Model = function() {
    var $this = this;

    // ...

    this.onLoadXhr = function() {
        if (_xhr.status === 200)
            _message.status = "success";
        else
            _message.status = "failed";

        var response = $this.parseResponse(_xhr.responseText);

        $this.setResponse(response);
    }

    // ...
}

The problem with the this is that it changes according to the context. In this case, within a function, the this no longer refers to the object, but rather to the context of the function.

0

Now I understand, and I’ll explain what happened with a new case.

var Model = function() {
    this.teste = 1;

    this.firstFunction = function (url) {
        this.secondFunction();
        this.thirdFunction();
    }

    this.secondFunction = function (url) {
        console.log("second " + this.teste);
    }

    this.thirdFunction = function (url) {
        console.log("third " + this.teste);
        var a = this.fourthFunction;
        a();
    }

    this.fourthFunction = function (url) {
        console.log(this);
        console.log("fourth " + this.teste); //undefined
    }

}

var model = new Model();
model.firstFunction();

The fourthFunction lost context by being passed as reference, assuming the context of the global object.

As a rule, always when you register a function to be executed later, either because of a CB, or Event or even to rename a Function you will have to bind it to the original scope, because if it will not take the target scope, ie in your case the onLoadXhr (no bind) is assuming this as that function itself and no longer this from openXhr.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.