Why this
gets undefined in high order functions in Javascript?
When a function has been used as a high order function (passed as an argument) they lose their awareness of this
, even if they are in the same object. E.g:
> function Person(name, gender) {
this.name = name;
this.sayHi = function (descriptor) {
return `Hi ${descriptor()}`;
}
this.getName = function() {
return this.name;
}
}
> p = new Person("Jon");
> p.sayHi(p.getName);
'Hi undefined'
A quick way to solve this issue is by passing the function bound to the object. E.g.:
> p.sayHi(p.getName.bind(p));
'Hi Jon'
But wouldn’t be great to understand the source of this problem?
A matter of context
Why this happens? Well, in Javascript when you assign a function of an object to a variable/parameter, you are actually detaching that method from its object:
> const myFunc = instanz.myFunction;
// or
> function myFunc2(f) {
f();
}
In such case, inside myFunction
the value of this
is not the same as intanz
anymore, but it will get the value of this
from the global context, which in strict-mode is undefined
instead of the global window
object.
Well, it doesn’t look very clear at the beginning, but with a short example it will be easier to understand.
An example to test the theory
-
Let’s create a test class that is aware of its context
> function Soldier(numBullets) { this.numBullets = numBullets; this.checkStatus = function() { "use strict"; console.log(this === captain); console.log(`I am ${this}`); console.log(`I have ${this.numBullets} bullets`); } }
-
Create an instance
captain
to see whether it works as expected> captain = new Soldier(5); > captain.checkStatus(); true I am [object Object] I have 5 bullets
-
Let’s see what happens if we separate the function
checkStatus
from its objectcaptain
:> captainStatus = captain.checkStatus; > captainStatus(); false I am undefined Uncaught TypeError: Cannot read property 'numBullets' of undefined
This confirms that checkStatus
is detached from captain
and that this
is undefined
.
Does this happen every time or only when it is in strict mode
? If the directive strict mode
is removed and you detach the function from its object again, then running this in your browser should get the global object window
:
> function Soldier(numBullets) {
this.numBullets = numBullets;
this.checkStatus = function() {
console.log(this === captain);
console.log(`I am ${this}`);
console.log(`I have ${this.numBullets} bullets`);
}
}
> captain = new Soldier(5);
> captainStatus = captain.checkStatus;
> captainStatus();
false
I am [object Window]
I have undefined bullets
This happened because in the browser window
was the object that owned captainStatus
at the moment of its execution. Try these same instructions again, but this time in a nodejs console to see what happens. Who is the owner of the method this time?
Conclusions
Each time we are going to execute functions in Javascript, it is important to remember that:
- A method can be detached from an object into a separated variable, e.g.
const oprhan = obj.someMethod
. - How we invoke a function changes the context on which it is executed.
- During a function invokation, i.e. the invokation of a function that is not attached to an object,
this
is going to be searched in the global context. - The keyword
this
isundefined
in a function invocation in strict mode.
It gets even more interesting when you learn that when using arrow functions in these scenarios you actually get a static context on different invokation types.
Leave a Comment