Why did the context change?

3

I'm doing my Javascript studies, and I'm doing some pretty simple to consume an API, but I got an error. Before commenting, 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

Notice that it arrives in this part it gives this error when calling the function. By printing this I checked that it is not from the context of the function, but from _xhr . Because ? In another part of the code I do a similar operation and this does not happen _xhr.onload = this.onLoadXhr; . Here I have referenced onLOadXhr within openXhr .

    
asked by anonymous 06.12.2016 / 19:43

3 answers

5

The problem is that _xhr.onload will be run by the Ajax instance 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 this way: _xhr.onload = this.onLoadXhr.bind(this); , this 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');
    
06.12.2016 / 19:52
1

The 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 this is that it changes according to context. In the case, within a function, this no longer refers to the object, but to the context of the function.

    
06.12.2016 / 20:06
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 has lost the context when passed as reference, assuming the context of the global object.

As a rule, whenever you register a function to run 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 it will not assume the target scope, ie in your case the onLoadXhr (without bind) is assuming this as that function itself and no longer the openXhr this.

    
12.12.2016 / 16:28