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