"This" in Javascript Explained


Ever heard this
in javascript? Yeah, that weird keyword. When i dedicate my time learning Javascript fundamental, this
make me scratch my head a lot.
If you’re from React engineer, you might be used it in React Class Component era (this.state.count
remember?), and we might be don’t care what it actually does. If i successfully get my state, so what?🤷♂️
But if you’re preparing for frontend interview, chances are you’ll get asked about this
. So in this article, i want to share my learnings with some case study as we go deeper to fully understand how this
works.
At the end of this article, i also give you some exercise and explained answer. Try to solve by yourself, predict the output, explain it out loud!💪
So, what is this
?
The keyword this
refers to the value of the object, depend on where this
is called. It can be called anywhere: in the global context, function, and a method wether in object or class. this
in each of them behaves differently and thus will have a different value.
confuse?😆 don’t worry, keep reading. I have some trick for you later. For now, let’s assume that this
refer to an object. The question is, what object? let’s dive in!
this
inside Global Context
Global context is where your code is not inside any function, class, or block statement. When you call this
in the global context, it refer to global object. The object is depends on your environment you run your code: global
in Node, window
in Browser
this.name = "Panda"
console.log(this.name); // output: Panda
console.log(this); // output: { name: "Panda", ...global (or window) }
Keep in mind, in simple terms, the most outer object that this
can refer to is a global object.
this
inside Function Context
First we have to divide function into two: Regular Function and Arrow Function. Why? because this
in these two behave differently.
Regular function is a function in Javascript before ES6 (ES5 and below).
Arrow Function is a new way to declare function in Javascript ES6.
We have to understand this
behavior in each one of these function declaration.
Regular Function
There is two way of calling Regular Function that this
will behave differently: pure function call and object method call.
Let’s see what pure function call does
function getPerson() {
this.name = "John"
console.log(this.name)
}
getPerson() // output: John
In non-strict mode, when you call a function, this
inside that function refer to global object. So this.name = “John”
will set the global property name
to John.
To prove it, run these code below
function getPerson() {
this.name = "John";
console.log(this.name);
}
function getName() {
console.log(this.name);
}
getPerson(); // output: John
getName(); // output: John
You can see both output is John. getName
didn’t even declare this.name inside the function. Lets break it down:
This is non-strict mode
getPerson()
is calledthis.name = “John”
is set to global context.console.log(this.name)
→ this try to findname
in global context. Found John, then print itgetName()
is calledconsole.log(this.name)
→ this try to findname
in global context. Found John (set ingetPerson()
call), then print it
Let’s twist a bit, how about this?
function getPerson() {
this.name = "John";
console.log(this.name);
}
function getName() {
this.name = "Ryan";
getPerson();
}
getName(); // output: John
Pay attention to the execution order, lets break it down:
This is non-strict mode
getName()
is calledin
getName
function →this.name = Ryan
is set. Now name is Ryan.getPerson()
calledin
getPerson
function →this.name = John
is set. Now name is replaced with Johnconsole.log(this.name)
→ will print John — recall of name is replaced from Ryan to John
Remember that this
in pure function call refer to global object, period. If you see 2 function call like code snippet above, just pay attention to the execution order.
However, if the function called as a method of an object, this
refer to that object. This is often called implicit bound, where this
is implicitly bound to an object.
const person = {
name: "John",
getPerson() {
console.log(this.name)
}
}
person.getPerson() // output: John
Even though we wrap the method call with other function, we still get the same result. No matter how nested the wrap it is.
const person = {
name: "John",
getPerson() {
console.log(this.name);
},
};
function getName() {
this.name = "Panda";
person.getPerson(); // this.name not replaced to Panda
}
getName(); // output: John
Remember this :
In regular function,
this
value depends on HOW function is called.
When function called as a pure function call, this
refer to the global object. When function called as a object method, this
refer to its object.
Lets practice before we jump into arrow function. Guess the output carefully
const person = {
name: "John",
getPerson() {
this.name = "Brian"
function getName() {
console.log(this.name)
}
getName()
}
}
person.getPerson() // output: ??
If you guess undefined
, congrats! you understand it well enough until here👍 Let’s break it down :
This is non-strict mode
person.getPerson()
is calledin
getPerson
→this.name = “Brian”
is set. Remember, this is a method call. Sothis
refer to its object, in this caseperson
object. Soperson.name
is replaced from John to BriangetName()
is calledIn
getName
→console.log(this.name)
. Remember how function called,this
in regular function call always refer to global contextbecause we haven’t set anything in global context,
this.name
will printundefined
Arrow Function
Arrow function do not have their own this
. Instead, they lexically bind this
from its surrounding context. For example, see this code snippet
const person = {
name: "John",
getPerson() {
console.log(this.name);
},
getName: () => {
console.log(this.name);
},
};
person.getPerson(); // output: John
person.getName(); // output: undefined
getName
is not bind into person
, it bind into its lexical scope. In this code, no surrounding lexical scope found by getName
. So this
refer to global object.
If you want to fix the undefined
, you can wrap it with regular function.
const person = {
name: "John",
getPerson() {
console.log(this.name);
},
getName() {
const arrowFunc = () => {
console.log(this.name);
};
arrowFunc();
},
};
person.getPerson(); // output: John
person.getName(); // output: John
getName
will be arrowFunc
surrounding lexical scope. Because getName
is called as a person
method, so this
bind to person
object. Then this.name
would be John.
this
inside Constructor Function
When function is in instantiated with new
keyword, this
is refer to newly created object from the constructor. For example
function Cat(name, breed) {
this.name = name;
this.breed = breed;
}
const myCat = new Cat("Boo", "British Short Hair");
console.log(myCat.name); // Output: Boo
console.log(myCat.breed); // Output: British Short Hair
this.name
and this.breed
is no longer refer to global context, unlike usual function we know before.
this
inside Class
When you define a method inside a class, that method is added to the class’s prototype. When you call it using an instance of the class, this
refers to that instance.
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hi, I'm ${this.name}`);
}
}
const p = new Person("Ali");
p.greet(); // Hi, I'm Ali
💡Gotchas
One thing you must remember. When working with class, javascript implicitly uses “strict mode”
. Let’s make a little twist to prove it
class Person {
constructor(name) {
this.name = name;
}
greet() {
function speak() {
console.log(`Hi, I'm ${this.name}`);
}
speak();
}
}
const p = new Person("Ali");
p.greet();
Theoretically, code snippet above would print undefined
. Thats because:
new Person()
instantiated, and assigned top
p.greet()
calledinside
greet
function →speak()
calledinside
speak
function → we callthis.name
because
speak()
is not a method call,this
should refer to global context. So it will printundefined
However…. javascript implicitly use strict mode when working with class. And the code should like this under the hood
'use strict';
class Person { ... }
So, that code snippet will throw error
TypeError: Cannot read properties of undefined (reading 'name')
So be careful if you face a code snippet like this. Remember, class always use strict mode.
Let’s Practice!
Exercise for you is simple:
Predict the output of given code snippet.
Why is that? Explain it out loud!
Give yourself a try, don’t directly go to solution. Just remember the fundamental and i’m sure you good to go. Good luck😉
Exercise #1
const person = {
name: "John",
getPerson() {
console.log(this.name)
}
}
const student = person.getPerson
student() // output: ??
Explanation
Remember! when comes to regular function, this
value depends on HOW function is called. Lets break it down:
this is non-strict mode
there is person object with getPerson method
declare variable
student
, then assignperson.getPerson
to it. Here,getPerson
is detached fromperson
object. Make it standaloneconst student = person.getPerson
student()
is called. Pay attention, student is called as standalone function → pure function call.because its a pure function call,
this
refer to global object — recall whenperson.getPerson
is detached fromperson
this
try to findname
in global contextname
is not declared yet in global context → then printundefined
Output:
undefined
Exercise #2
const person = {
name: "Morgan",
greet: function () {
console.log("1:", this.name);
function inner() {
console.log("2:", this.name);
}
const arrow = () => {
console.log("3:", this.name);
};
inner();
arrow();
},
};
person.greet();
Explanation
lets break down the code:
const person = {
name: "Morgan",
greet: function () {
console.log("1:", this.name); // this refer to person
function inner() {
console.log("2:", this.name); // this refer to global object
}
const arrow = () => {
console.log("3:", this.name); // this refer to this from greet method.
};
inner();
arrow();
},
};
this is non-strict mode
inner()
is regular function → this depend on how the function is called.arrow()
is arrow function → this lexically inherited fromgreet
, which is bound toperson
object
Output:
Morgan
undefined
Morgan
Exercise #3
const person = {
name: "John",
getPerson() {
console.log(this.name)
}
}
const student = person.getPerson
function getName() {
this.name = "Frank";
student();
}
getName(); // output: ??
Explanation
this is non-strict mode
getName is called
in getName function:
this.name = “Frank” is set. this in regular function refer to global context, so name property in global context will be Frank
student is called
when student is called, same as exercise #1, pay attention to student variable. getPerson binding is detached from its person object. Now its just standalone function
in getPerson function → console.log(this.name), this trying to find name. normally, this will refer to person. But just recall that this method call is detached from its object, this will refer to global object
we know recently we name property in global object to Frank, so this.name in getPerson refer to Frank
Ouput:
Frank
Subscribe to my newsletter
Read articles from Aligar Syahan Putra directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
