this
and arrow functions
In JavaScript, this
is a variable that’s set when a function is called. This makes it a very powerful and flexible feature, but it comes at the cost of always having to know about the context that a function is executing in. This is notoriously confusing, especially when returning a function or passing a function as an argument.
Let’s look at an example:
let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function() { return function() { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); return {suit: this.suits[pickedSuit], card: pickedCard % 13}; } } } let cardPicker = deck.createCardPicker(); let pickedCard = cardPicker(); alert("card: " + pickedCard.card + " of " + pickedCard.suit);
Notice that createCardPicker
is a function that itself returns a function. If we tried to run the example, we would get an error instead of the expected alert box. This is because the this
being used in the function created by createCardPicker
will be set to window
instead of our deck
object. That’s because we call cardPicker()
on its own. A top-level non-method syntax call like will use window
for this
. (Note: under strict mode, this
will be undefined
rather than window
).
We can fix this by making sure the function is bound to the correct this
before we return the function to be used later. This way, regardless of how it’s later used, it will still be able to see the original deck
object. To do this, we change the function expression to use the ECMAScript 6 arrow syntax. Arrow functions capture the this
where the function is created rather than where it is invoked:
let deck = { suits: ["hearts", "spades", "clubs", "diamonds"], cards: Array(52), createCardPicker: function() { // NOTE: the line below is now an arrow function, allowing us to capture 'this' right here return () => { let pickedCard = Math.floor(Math.random() * 52); let pickedSuit = Math.floor(pickedCard / 13); return {suit: this.suits[pickedSuit], card: pickedCard % 13}; } } } let cardPicker = deck.createCardPicker(); let pickedCard = cardPicker(); alert("card: " + pickedCard.card + " of " + pickedCard.suit);
Even better, TypeScript will warn you when you make this mistake if you pass the --noImplicitThis
flag to the compiler. It will point out that this
in this.suits[pickedSuit]
is of type any
.
Please login to continue.