The call(), apply(), and bind() methods work as expected with traditional functions, because we establish the scope for each of the methods:
const obj = {
num: 100,
};
globalThis.num = 42;
const add = function (a, b, c) {
return this.num + a + b + c;
};
console.log(add.call(obj, 1, 2, 3));
console.log(add.apply(obj, [1, 2, 3]));
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3));
With arrow functions, since our add function is essentially created on the globalThis (global) scope, it will assume this is the globalThis.
const obj = {
num: 100,
};
globalThis.num = 42;
const add = (a, b, c) => this.num + a + b + c;
console.log(add.call(obj, 1, 2, 3));
console.log(add.apply(obj, [1, 2, 3]));
const boundAdd = add.bind(obj);
console.log(boundAdd(1, 2, 3));
Perhaps the greatest benefit of using arrow functions is with methods like setTimeout() and EventTarget.prototype.addEventListener() that usually require some kind of closure, call(), apply(), or bind() to ensure that the function is executed in the proper scope.
With traditional function expressions, code like this does not work as expected:
const obj = {
count: 10,
doSomethingLater() {
setTimeout(function () {
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater();
With arrow functions, the this scope is more easily preserved:
const obj = {
count: 10,
doSomethingLater() {
setTimeout(() => {
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater();