That is because when you do [].forEach(this.sayBar)
, the this
actually refers to the windows object. When you invoke this.sayBar
using an arrow function, then you pass on the lexical this (which is the object itself) to the function.
Note: If passing the callback function uses an arrow function expression, the thisArg parameter can be omitted, since all arrow functions lexically bind the this value.
Source
For this very reason, that is why Array.prototype.forEach
accepts thisArg
as the second positional argument, so you can do [].forEach(this.sayBar, this)
and it will work:
const foo = {
bar: 'bar',
sayBar() {
console.log(this.bar);
},
sayBar3times() {
// this doesn't work:
[1, 2, 3].forEach(this.sayBar);
// this works:
[1, 2, 3].forEach(this.sayBar, this);
// this works:
[1, 2, 3].forEach(() => {
this.sayBar();
});
},
};
foo.sayBar3times();
With regards to your updated code, the answer is the same:
Test A does not bind the foo
in the callback, so this
refers to the global object window
. This can be fixed by simply doing [1, 2, 3].forEach(foo.sayBar, foo)
. In this case, the this
inside the method call will now refer to the foo
object.
Test B does work because the sayBar3times
lacks binding context in the forEach()
as well. So, if you provide thisArg
correctly, by doing [1, 2, 3].forEach(this.sayBar, this)
it will work
In both examples, you will need to provide a thisArg
to the callback in Array.prototype.forEach
:
const foo = {
bar: 'bar',
sayBar() {
console.log(this.bar);
},
sayBar3times() {
[1, 2, 3].forEach(this.sayBar, this);
},
};
/* test A */
[1, 2, 3].forEach(foo.sayBar); // doesn't work
[1, 2, 3].forEach(foo.sayBar, foo); // works
/* test B */
foo.sayBar3times(); // works