What is the meaning of ‘this’?
As a newbie to JavaScript, the concept of this
has been bewildering at times. The keyword this
can mean different things for ES5 functions than it does for ES6 (arrow) functions.
After three weeks of using the language, I’ve come to learn a good rule of thumb: always use arrow functions. But I was curious why there is a difference so I looked into it.
It turns out that arrow functions use what’s called lexical scoping. This means that the function always defines its this
by where the function was declared. If it was declared in the global scope, this
will always refer to the global scope. If it was declared within an object, this
will always refer to the object.
Furthermore, if an arrow function foo
is declared within another function bar
, foo
will define this
based on what bar
's this
is.
ES5 functions are not this straightforward. ES5 functions define this
based on what is executing them. If an ES5 function called foo
is declared in the global scope but invoked within another function bar
, the original function foo
’s this
will be the function in which it was invoked, bar
. If foo
is used as a callback for an event listener, its this
will actually be the node on which the listener was set. The definition of this
relies on the context in which the function is invoked.
This has some interesting and unsuspected consequences. Take the below example:
If we create a new instance of the object Person
like this: const mike = new Person(“Mike”, “Sup?”)
and invoke the function mike.saySaying()
, we get the expected result of logging “Hey!” to the console.
But when we try invoking the second method, speak()
, something strange happens.
The function speakSaying()
doesn’t have access to the this.name
property in the Person
object because its this
is actually the function speak()
, and speak()
doesn’t have a this.name
property. This is a little perplexing, but never fear: arrow functions are here to save the day.
If we change the speakSaying()
function to arrow notation, the problem is fixed.
Now, invoking the function on our object instance produces the expected result.
The arrow function retains the concept of this
in which it was declared.
If we want to keep the function in ES5 notation, we can fix this issue instead by using the Function.prototype.bind()
method. This method allows us to define the function’s this
ourselves.
After we declare the function speakSaying()
, we bind it to the speak()
method’s this
, which is the Person
object, causing the speakSaying()
function to behave in the same way it did using arrow notation.
This would also work if the speakSaying()
function is declared outside of the Person
object.
However, the bind()
method will not change an arrow function’s this
. This means that ES5 function notation is useful if you need control over a function’s definition of this
. Most of the time, though, it’s best to stick with arrow functions.