What's the this value in callback arrow functions?

295 Views Asked by At

So as I am reading on the execution context in javascript and how does it works, everything seemed fine until I tried some examples on the this value of callback arrow functions.

const printCard = function () {
  this.signatories.forEach((signatory) =>
    console.log(this) /* expected to return signatories, returned messageConfig */
  );
};

const messageConfig = {
  closing: {
    Thor: "Admiration, respect, and love",
    Loki: "Your son",
  },
  signatories: ["Thor", "Loki"],
};

printCard.call(messageConfig);

Here since forEach is prototypal method of signatories, I expected the callback (the arrow function) to automatically take the this value from the enclosing context (forEach in this case). However, it seemed to return the messageConfig which reminds me of lexical scoping a little bit.

2

There are 2 best solutions below

0
Alexander Nenashev On

An arrow function just borrows this and arguments from the outer scope (it doesn't have own). The outer scope is printCard() which is called with this = messageConfig, and arguments arg1 and arg2. You can access the array with this.signatories or use the third argument of forEach() which is the array of being iterated.

Arrow functions are useful as callbacks inside methods since you have this inside them as an instance a method is called on, so no need in bind/call/apply/self as with a usual function.

const printCard = function () {
  this.signatories.forEach((signatory, i, arr) => {
    console.log(arguments);
    console.log(this.signatories);
    console.log(arr);
  });
};

const messageConfig = {
  closing: {
    Thor: "Admiration, respect, and love",
    Loki: "Your son",
  },
  signatories: ["Thor", "Loki"],
};

printCard.call(messageConfig, 'arg1', 'arg2');

2
Barmar On

Array.prototype.forEach() doesn't pass the array as the this context to its callback function. It passes the array it's looping over as the third argument. So if you want to get signatories, use

const printCard = function () {
  this.signatories.forEach((signatory, i, signatories) =>
    console.log(signatories);
  );
};

You can optionally pass a second argument to forEach() and this will be passed as the this context.

However, arrow functions don't accept a passed this context, so that wouldn't help here. They inherit the context from the defining scope. Since you're binding the context to messageConfig when you use printCard.call(), that's the value of this in printCard() and also the callback function.

If you were to change the callback function to a regular function, this would be the window object in a browser, or the global object in node.js, unless the code is in strict mode.