This is a common problem, which can be solved with Arrow functions or with .bind()
.
Abridged:
a) Why this reference loss occurred?
When we run a function such as callback of a Promise the execution context (the this
) mute to undefined
or the global object (window
in the browser).
b) Why the use of Arrow functions has solved the problem?
This is one of the novelties (new possibility) with Arrow Function, it runs in the context of where it is declared.
c) There would be another way to circumvent this reference loss in the above example?
Yeah, you can use the .bind()
to force the execution context. O .bind()
creates a copy of the function, without calling it, and when it is called runs with the context set in .bind(contexto)
.
Example:
return service.save({ id }).then(this.onSuccess.bind(this), this.onError.bind(this));
Longer explanation:
The execution context of a function depends on several things. The general rule is that the execution context is the object/class to which the function belongs. I talked about it in another question. In the case of normal functions an example may be:
Example:
function teste() {
console.log('contexto de teste é foo?:', this == 'foo');
log();
}
function log() {
console.log('contexto de log é foo?:', this == 'foo');
}
teste.call('foo');
Despite the function teste
run in a specific context, log
will take place in the context of window
.
Functions passed as callback to a Promise are executed in another execution context. Thus, pass a function such as callback to the .then()
does not guarantee the execution context. This does not apply to Arrow functions declared inline.
Promise has two modes of operation with regard to callback execution context:
- if you are in
strict mode
If we are in strict mode
or the function used as callback implemente strict mode
, then the this
will be undefined
. This is your case as ES6 class methods always run in strict mode
.
- if you are not in
strict mode
If we’re not in strict mode
nor the function used as callback implemente strict mode
, then the execution context will be the global object, window in the case of the browser.
class Classe {
constructor(){
this.teste();
Promise.resolve().then(this.teste);
}
teste() {
console.log(this);
}
}
new Classe();
b) Why the use of Arrow functions has solved the problem?
This is one of the advantages and differences between functions we already know, declared with function
. To Arrow Function will always have as the context of execution the surrounding context where it is inserted.
Example:
class Classe {
constructor() {
const logA = () => {
console.log('logA', this);
};
const logB = function() {
console.log('logB', this);
}
logA();
logB();
}
}
const logC = () => {
console.log('logC', this);
};
const logD = function() {
console.log('logD', this);
}
new Classe();
logC();
logD();
In this example (jsFiddle) the results are:
logA // dá Classe {}
logB // undefined
logC // window
logD // undefined
That is to say, Arrow functions use the context around them, while "old-fashioned" functions receive the context of the object to which they belong.
Good afternoon Dorival, just to clarify: the functions
onSuccess
andonError
are not being executed, or are executed without reference to the class?– mrlew
Good afternoon @mrlew, the functions are not executed in the first case due to lack of reference, which does not happen in the second with the use of Arrow Function, so in the second case they perform normally. But there are cases where functions can be performed without reference?
– Rafael Kendrik
Interesting question! Related to: http://answall.com/a/108745/129. I am in a hurry but I will reply later.
– Sergio
Beauty! I look forward to and thank you for sharing the link Sergio
– Rafael Kendrik
@Dorivalzanetto your service is returning a Promise as per the document on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise ? Because I did a test and the result was expected according to the original scenario.
– dikey
Two other solutions, if you are using Babel enabling the
stage-2
of the new proposals. https://github.com/andreypopp/autobind-decorator and https://github.com/tc39/proposal-class-public-fields– Gabriel Katakura
@Dorivalzanetto I left reply and I was giving a touch. It was this information that you were looking for?
– Sergio
Yes @Sergio, was very enlightening. Congratulations for the contents presented in the reply and thank you!
– Rafael Kendrik
I am using Babel as transpile yes and I will give one in the links, thanks @Gabrielkatakura!
– Rafael Kendrik
One more solution: http://blog.jeremyfairbank.com/javascript/javascript-es7-function-bind-syntax/
– Gabriel Katakura