In many card games, there are certain cards or combinations of cards that "beat" or "trump" others. For example, in poker, a straight beats a three of a kind, and a three of a kind beats a two of a kind. Likewise, in the case of ties between two similar hands, an Ace will beat a King, and so on and so forth.
Precedence in programming languages is just like this. In regards to this context, we know quite a couple different ways that this context can be set - first there's the normal way (set dynamically as method on an object), then there's the new
keyword, then also call
, apply
and bind
, and now arrow functions. But what happens if these different methods collide? For example:
const Dog = (name, breed) => {
this.name = name;
this.breed = breed;
}
const cody = new Dog('Cody', 'pug');
Does the lexical this
set by the arrow function cause this
to refer to the global context? Or does the new
keyword set it to be a brand new object that the Dog function will return?
Another example (without arrow functions at all):
const Cody = { name: 'Cody' };
const Lexie = { name: 'Lexie' };
const sayName = function () {
console.log(this.name);
}
const boundSayHello = sayName.bind(Lexie);
Cody.sayName = boundSayHello;
Cody.sayName();
Whose name will come out? Cody's (because sayName
is invoked as a method on the Cody object), or Lexie's (because Cody.sayName refers to a function that is bound to the Lexie object)?
There are many situations like this, but fortunately figuring them out is as simple as learning the rules to a card game. Whenever two or more different ways of setting this
context "fight", the winner is resolved in the following order:
- If the function is an arrow function, then lexical
this
context wins. Hands down, no exceptions. - If the function uses the
new
keyword, thenthis
refers to the new object that gets constructed. - If a function is bound using
bind
, thenthis
will refer to the bound object. Note that functions cannot be "re-bound" - if you create a bound function, and then try to bind the bound function to another new context, the original context will win. - If the function sets
this
context explicitly usingcall
orapply
, then the object that was explicitly specified by one of those methods wins. - If the function is invoked as a method on an object (the object in this case may also be referred to as the call-site of the function), then the object will be
this
. - If none of the above apply, then
this
will be refer to the global context (ex.window
in the browser), or if you're using'use strict'
, then it will beundefined
.
To re-iterate, the order above matters. That is, arrow functions will beat the new
keyword, which beats bind
, which beats call
and apply
, which beats dynamic call-site, which beats nothing at all.
Using these rules, what do you think this
refers to in the two code examples above (fig 1 and fig 2)?
In fig 1, the answer is the global context set by the arrow function! Arrow functions are even more powerful than the `new` keyword! This means you should never use an arrow function as a constructor function.
In fig 2, the answer is "Lexie". While the dynamic call site of the function is Cody, the function that Cody invokes is bound to Lexie, and `bind` is higher on our list, so the context set by `bind` wins.