this
is one of the most difficult JavaScript concepts to understand, no doubt about that. If you understood it on your first exposure to it, you're probably one of the greatest JavaScript prodigies to ever walk this earth. Although, if you can't seem to wrap your head around it, don't worry, I'd make you a prodigy.
In this article, You are going to see a substantial amount of code snippets and visual illustrations, this is just to give you a better understanding of JavaScript this
keyword. The code snippets are not complex, I promise.
You will learn
What
this
means in JavaScript and how it works under the hoodthis
in the global scopethis
in functions and arrow functionsthis
in object methodsthis
in constructor functionsthis
in classesthis
in event handlersBest practices when using
this
Imagine you are at a smartphone store trying to purchase the latest Samsung device. You found the exact Samsung device and went to the checkout line to finalize the purchase. As you approach the cashier, you noticed a long queue of people waiting to make their own purchases.
After a few minutes, it got to your turn and you show the cashier the Samsung phone you want to purchase, the cashier then begins the checkout process. As the cashier scans the phone and begins to process your payment, you notice that he keeps using the word "this". The cashier writes some stuff on the receipt like "this phone is the latest model" and "this purchase was paid through cash". It made sense to you because he was referring specifically to the phone you were purchasing, not any other Samsung device in the store.
In JavaScript, this
works in a similar way. When you call a function using an object, this
refers to the object. So, just as the cashier uses "this" to refer to the specific Samsung device you want to buy, this
in your JavaScript code refers to the object that invoked the function.
In the illustration above, when the Samsung object calls the function, this
in that function references the Samsung object; when the iPhone object calls the function, this
references the iPhone object.
this
refers to the object that calls the function at the time of the function call. this
keyword always refers to either an object, undefined or null. Nothing else.
The code snippet below is a quick example of how this
works -
function thisValue() {
return this;
}
const newPhone = {
name: "Samsung S23 Ultra",
price: "$900",
};
newPhone.thisValue = thisValue;
console.log(newPhone.thisValue());
// Output: newPhone {name: "Samsung S23 Ultra", price: "$900", thisValue: ƒ}
The thisValue
function was added to the newPhone
object as a method (functions in an object). Therefore, when the newPhone
object invokes the thisValue
method, it returns newPhone
since this
refers to the object that calls a function.
Now, let's imagine you want to access the brand of the newPhone
object, you could create a method that returns the newPhone
brand, like this newPhone.name
(works!) or you could say this.name
(also works!)
const newPhone = {
name: "Samsung S23 Ultra",
price: "$900",
thisName() {
return `I own the ${newPhone.name}.`; // using object name
},
};
// or
const newPhone = {
name: "Samsung S23 Ultra",
price: "$900",
thisName() {
return `I own the ${this.name}.`; // using this keyword
},
};
However, there's a slight problem. Since everything in JavaScript (e.g array, functions, etc.) are all objects under the hood, there are times you'd want an object to be created from another object. For example, an object called oldPhone
can be created from an existing object newPhone
, like this
const newPhone = {
name: "Samsung S23 Ultra",
brand: "Samsung",
touchScreen: true,
thisName() {
return `I own the ${newPhone.name}.`;
},
};
//Object.create() creates an object based on an existing object.
const oldPhone = Object.create(newPhone);
oldPhone.name = "Samsung J5";
console.log(oldPhone.thisName()); // outputs: ???
What do you think oldPhone.thisName()
would output? It outputs: "I own the Samsung S23 Ultra." which shouldn't be because we need the oldPhone
name and not the newPhone
name. Since thisName()
method is already explicitly tied to only return newPhone
name, trying to call the function with another object (in this case oldPhone
object) would still return newPhone.name
.
To correct this, thisName()
method should reference the name property using this.name
rather than newPhone.name
so that it works correctly with any object that has a name property.
const newPhone = {
name: "Samsung S23 Ultra",
brand: "Samsung",
touchScreen: true,
thisName() {
return `I own the ${this.name}.`; // changed to this.name
},
};
const oldPhone = Object.create(newPhone);
oldPhone.name = "Samsung J5";
console.log(oldPhone.thisName());
// outputs: I own the Samsung J5. Works as expected
Using this.name
instead of newPhone.name
is better because it allows the code to be more flexible and reusable. Just know that when you use this
in an object method, this
would mostly refer to the object that invokes the method (except for arrow functions, more on that later).
Did you know?
The
this
keyword is like a hidden parameter that is passed to a function when it is called. You can set the value ofthis
to a specific object by using the dot notation (.) to prefix the function with the object. This way, the object that you specified will be used as thethis
context, for examplenewPhone.thisValue()
.It's a powerful feature of the language, but it can be tricky to use correctly, so make sure you understand it well.
this
In Global Scope
Before we begin, let's have a quick understanding of how this
works in JavaScript strict mode.
Strict mode is a mode that was introduced to JavaScript for the main purpose of eliminating silent errors and allowing developers to write clean code. You can apply strict mode to your JavaScript by adding 'use strict';
at the top level of your JavaScript i.e before you start coding or doing anything else or at the top level of a function block if you prefer not to apply it to your whole JavaScript code.
Whether in strict mode or not, this
keyword used at the top level of a script will always refer to the global object(or window object).
Although, when strict mode is used inside a function, and the function is invoked without an object attached to it, this
would return undefined instead of the global object as the normal JavaScript non-strict mode does. This makes it much easier to spot bugs in the long run.
Let's see how this
works in terms of JavaScript scoping. In the global scope, this
refers to the global object. In a browser environment, the global object is the window
object, while in a Node.js environment, it is the global object.
These options...
variables and object properties that are given the value
this
in the global scope,functions that are invoked in the global scope with no object attached to it,
...they all return the global object when assigned to the this
keyword.
let foo = this;
let foo1 = { value: this };
function foo2() {
return this;
}
console.log(foo === window); // true
console.log(foo1.value === window); // true
console.log(foo2() === window); // true
In the code above, the variable foo
has this
keyword assigned to it and since the variable was created in the global scope, the this
keyword can't refer to any other object so it defaults to the global object.
The variable foo1
is an object with one of its properties assigned to the this
keyword. It can be a little tricky since this
mostly references objects but you have to know that, it is only functions found in an object that works that way. Object properties (not object methods) that are assigned this
refers to the global object because this
is not inside a method, it's just in the global scope.
The foo2
function acts differently though. A function that returns the value of this
depends on how it's invoked; since the foo2
function was not invoked as a method of any object, the this
keyword in the function defaults to undefined (strict mode) or the global object (non-strict mode).
this
In Functions & Arrow Functions
Using this
in a normal function is actually straightforward, the this
keyword in the function would refer to the object that called it or the object it's bound to, a quick example -
//create a function that returns `this`
function playMusic() {
return `${this.name} is playing music.`;
}
//create objects
const newPhone = {name: "Samsung S23 Ultra"};
const oldPhone = {name: "Samsung J5"};
//copy the function as methods to the object created
newPhone.playMusic = playMusic;
oldPhone.playMusic = playMusic;
console.log(newPhone.playMusic()); //Samsung S23 Ultra is playing music.
console.log(oldPhone.playMusic()); //Samsung J5 is playing music.
When the function was copied as a method to the two objects, you might have thought "why not invoke it?". Well if the function was invoked before storing it inside the object, it is the returned value of the function that'd be stored in the object, not the function itself. In essence, doing that would just store a string to newPhone.playMusic
that'd output "[object Window] is playing music." because the function was called with no object attached to it so this
would default to the global object.
❕ Useful Tips
In functions, this refers to the dynamic context in which that particular function is called:
It can reference the object that invoked it.
It can be explicitly bound to a different object than the one that created it by using call(), apply(), or bind().
If invoked without the function binding to any object implicitly or explicitly, it references the global object.
Now, going through the steps of creating a function with this
keyword and copying it as a method to different objects can be quite stressful. Instead, you can just create the function as an object method in one object and make other objects call the object method anytime. To achieve this, Javascript has 3 in-built functions that can help with that:
call
apply
bind
These three functions can come in handy when you have an object method that uses this
keyword to reference and update stuff around that object, but you also need a similar object method like that for other objects. Therefore, rather than creating original object methods for each object, you can simply borrow the object method you need from the object that already owns it. These methods can surely save you a ton of time and effort when working with complicated objects or extensive codebases.
Using Call
The call function executes a function and explicitly set the this
keyword to reference a desired object. The syntax is call(thisArg, ...arguments)
where "thisArg" is the object you want this
keyword to refer to and "...arguments" are other arguments(separated by a comma) that the object method might need. For example
//object with the playMusic method
const newPhone = {
name: "Samsung S23 Ultra",
playMusic() {
return `${this.name} is playing music.`;
},
};
// objects that need the playMusic method
const momPhone = { name: "Samsung S10" };
const dadPhone = { name: "Samsung A80" };
//using playMusic method for other objects that needs it
console.log(newPhone.playMusic.call(momPhone)); //Samsung S10 is playing music.
console.log(newPhone.playMusic.call(dadPhone)); //Samsung A80 is playing music.
The code snippets above have three objects; two objects momPhone
and dadPhone
do not have a playMusic
method, only one object newPhone
has a playMusic
method. Now, obviously you'd want all phone objects to be able to play music but creating playMusic
method for each object can be tasking when dealing with lots of objects. Instead, we kind of took a copy of the playMusic
method from the object that has it and used it for the two other objects that needed it. The call()
method makes this
keyword inside the playMusic
method to reference the desired object that was passed as an argument to call()
, instead of the original object that created the method.
Using apply()
The apply function also invokes an object method that uses this
keyword and binds it to another object. Although, arguments to be used by apply()
are passed as a single array unlike call()
. The syntax is apply(thisArg, [...arguments])
where "thisArg" is the object you want this
keyword to refer to and "[...arguments]" are other arguments that are passed in an array form that the object method might need. For example,
//object with the playMusic method
const newPhone = {
name: "Samsung S23 Ultra",
playMusic(song, artist) {
return `${this.name} is playing "${song}" by ${artist}.`;
},
};
// objects that need the playMusic method
const oldPhone = { name: "Samsung J5" };
const dadPhone = { name: "Samsung A80" };
//using playMusic method for other objects that needs it
console.log(newPhone.playMusic.apply(oldPhone, ['Calm down','Rema'])); //Samsung J5 is playing "Calm down" by Rema.
console.log(newPhone.playMusic.apply(dadPhone, ['Bad', 'Michael Jackson'])); //Samsung A80 is playing "Bad" by Michael Jackson.
By using apply()
in the code above, we were able to use this
keyword from the playMusic
method to reference our desired objects rather than the object that owns the playMusic
method. We also passed in an array argument to tweak the result a little bit.
Differences between call() and apply()
They are both incredibly similar with just one difference, which is the way arguments are passed to them.
call() accepts arguments in list-comma separated form -
call(thisArg, arg1, arg2, arg3)
apply() accepts arguments in a single array-list form -
apply(thisArg, [arg1, arg2, arg3])
Yes, yes, I know what you're thinking. Why would you want to pass an array as an argument when you could just follow the normal way that
call()
uses?Well, there are instances when you'd need to use
apply()
instead ofcall()
. For instance,
The arguments to be used are being passed dynamically by a server so you don't know the fixed number of arguments to pass.
The arguments you want to use have already been stored in an array.
You might want to use the length of the arguments you passed.
Using
apply()
when you come across any of these scenarios would make your JavaScript journey easier.
Use bind()
If you noticed, when we were using the call()
and apply()
functions on the object method, the object method was invoked immediately to reference the desired object as the this
value. However, in some cases, it might be necessary to bind the this
value of an object method to a new object without invoking it immediately. In such situations, the bind()
function can be used.
The bind()
function creates a new function whereby the object method that uses the this
keyword would be bound to the desired object passed as an argument. The returned function can then be invoked anytime you wish and this
would always refer to the desired object that was bounded.
The bind()
syntax is bind(thisArg, ...arguments)
where the "thisArg" parameter is the desired object that this
should refer to & the "...arguments" parameters are the list of arguments(separated by commas) that the function might need.
One of the basic uses of bind() is to create a function that will always be called with a specific value of this
, regardless of how it is invoked. this
would never refer to any other object apart from the desired object it was bound to.
//object with the accessInternet method
const newPhone = {
name: "Samsung S23 Ultra",
accessInternet(hasAccess) {
if (hasAccess) {
return `${this.name} can access the internet.`;
} else {
return `${this.name} can't access the internet.`;
}
},
};
// object that needs the accessInternet method
const oldPhone = { name: "Samsung J5" };
//bind accessInternet method to another object using bind().
//and store the newly created function in a variable
const oldPhoneBound = newPhone.accessInternet.bind(oldPhone, false);
// "oldPhoneBound" function can now be called in any context and at any time,
// its `this` value would always refer to the "oldPhone" object
You can think of bind()
working like this -
const oldPhoneBound = (oldPhone) => newPhone.accessInternet.call(oldPhone);
JavaScript creates a function for you, then uses call()
to tie the object method this
to the desired object. Since codes placed in a function would not run until the function is invoked, bind()
makes use of that phenomenon
It creates a function,
In the new function body, it places a
call()
function that ties the object methodthis
to the desired object and returns itThe new function can then be stored in any variable and called at any particular time.
When to use bind()
For instance, when using this
in a setTimeout, it sometimes doesn't work as expected because setTimeout invokes the function you provided it automatically after a given time.
When setTimeout calls an object method that uses this
, the this
keyword would refer to the global object because setTimeout was what called the object method, not the object. Remember, this
in a function is determined by how the function is called. However, to always make sure this
points to the right object, bind()
comes to the rescue
function accessInternet(hasAccess) {
console.log(this)
}
const oldPhone = { name: "Samsung J5" };
oldPhone.hasAccess = accessInternet;
setTimeout(oldPhone.hasAccess, 5000, true); // Global object.
// "oldPhone.hasAccess" was called by setTimeout,
// `this` loses its context because...
// oldPhone didn't call the function, setTimeout did...
// `this` defaults to the global object.
//To fix this
function accessInternet(hasAccess) {
console.log(this)
}
const oldPhone = { name: "Samsung J5" };
//bind "this" to the desired object
oldPhone.hasAccess = accessInternet.bind(oldPhone);
setTimeout(oldPhone.hasAccess, 5000, true) //oldPhone Object
// If call() had been used, the function would have been invoked before you even get a chance to use setTimeout()
To avoid unexpected behavior, be cautious when passing functions that use this
keyword. Errors can occur if this
context is not properly bound to the object it needs to reference. Binding this
to the object before passing it around ensures that it always has the expected context.
Fun Fact
The bound function created by
bind()
can be further bound. Although no matter how many times it is rebounded,this
would always point to the initial object it bounded to. You can read more on that here.
Using this
In Arrow Functions
ES6 JavaScript introduced a new type of function called "Arrow Function", which can be written as let func = () => {}
.
const playing = () => true;
You've probably heard of this statement "Arrow functions don't have their own this
". You may wonder, "What does this mean?".
Arrow functions don't follow the rules their sibling(i.e normal functions) follows, whereby this
value is determined by how the function is called. In arrow functions, this
refers to the value of its surrounding lexical scope and if none, it defaults to the global object. this
in arrow function is set to what it was when the arrow function was made.
Therefore,
- If an arrow function is created in the global scope,
this
value refers to the global object e.g
// Create an arrow function that returns the value of `this`
const arrowFunc = () => this;
// An object with a `name` property
const newPhone = { name: "Samsung S23 Ultra" };
// Add a method to the object that is set to `arrowFunc`
newPhone.method = arrowFunc;
// call `newPhone.Method`
console.log(newPhone.method()); //global object
// The arrow function is called,
// but since `arrowFunc` was created in the global scope,
// `this` has been binded by default to the global object,
// even though it was called as a method of the `newPhone` object.
- If an arrow function is used as an object method,
this
refers to the enclosing lexical scope of the arrow function.
const newPhone = {
name: "Samsung S23 Ultra",
thisValue: this,
arrowFunc: () => this, //arrow function as object method
};
console.log(newPhone.thisValue); // Output: global object.
console.log(newPhone.arrowFunc()); // Output: global object.
// In this example, `this` in "arrowFunc"...
// is similar to `this` in "thisValue" property...
// which is the enclosing lexical scope,
// in this case the global object.
// Remember, objects themselves do not have a scope, only functions and blocks in the object are scoped.
- If an arrow function is created inside a normal function,
this
value is determined by how the normal function is called e.g
const newPhone = {
name: "Samsung S23 Ultra",
arrowFuncInFunction() {
let foo = () => this;
// "foo" is an arrow function in a normal function
return foo();
},
};
console.log(newPhone.arrowFuncInFunction()); // Output: newPhone object.
// The arrow function inside "arrowFuncInFunction" method...
// returns the `this` value of its enclosing lexical scope,
// which would be determined by how "arrowFuncInFunction" is called.
- If an arrow function is invoked using
call(), apply(), or bind()
, the "thisArg" passed is discarded because the arrow functionthis
has already been bounded to thethis
context of its lexical scope.
// Create an arrow function that returns the value of `this`
const arrowFunc = () => this;
// An object with a `name` property
const newPhone = { name: "Samsung S23 Ultra" };
// use call() to bind `this` to newPhone
console.log(arrowFunc.call(newPhone)); //global object
// This did not work as expected because,
// the "arrowFunc" has automatically bounded `this`...
// to its lexical scope when it was created in the global scope.
// Therefore trying to rebind it using call() would have no effect,
// the output is still the global object.
this
In Object Methods
Objects Methods in simple terms are functions that are found in objects. Let's use the Samsung phone you purchased earlier as an example, the phone is an object that has object methods(i.e functions) like the ability to play games, watch videos, chat with your ex etc.
Did you know?
Object curly braces {} are not enclosing lexical scope. They just happened to be used by JavaScript a long time ago before scoping was introduced. They do not define a new scope, only the methods in an object creates a new scope.
Now, when using this
with objects properties that aren't methods, this
refers to the global object, while this
in object methods refers to how the object method is called.
const newPhone = {
// object properties that aren't methods
name: "Samsung S23 Ultra",
thisPhone: this,
// object method
thisPhoneMethod() {
return this;
},
};
console.log(newPhone.thisPhone); // global object
console.log(newPhone.thisName()); // newPhone objet
// "newPhone.thisPhone" property points to the window object...
// because `this` can not be assigned as a value...
// "newPhone.thisPhoneMethod" points to the "newPhone" object...
// because the method was called with the newPhone object.
Remember, objects can use the method of another object as their own method. This can be done by directly storing an object method as a property of the desired object, by prototype inheritance, or by using call, apply and bind
functions.
📍 Note
Just because a function is created inside an object does not mean the value of
this
in that object method would always refer to the object.this
in a particular object method can refer to any object that calls that object method.
Using this
in callbacks
Oh callbacks, a concept in JavaScript that's also a little bit difficult to understand. Well, for those who don't know, callbacks are basically functions that can be called by another function when passed as an argument to that function, hence the name callbacks.
For example, setTimeout accepts a callback function. When setTimeout is called, it automatically invokes the function you passed as an argument at the time specified. Most in-built array methods like map, and forEach all need a callback function as an argument which they execute as soon as they are called.
In a nutshell, closure gets into a function as an argument, the function invokes the callback when called. That's all.
Now, this
in callbacks are relatively simple, they depend on how the callback is called. If you try to attach an object to a callback, this
would refer to the context in which the function that executes the callback is called. For example
// custom function to execute callback functions
function invokeCallback(callback){
return callback();
}
const human = {
name: "Eren Yeager",
getObject() { return this },
};
invokeCallback(human.getObject); // output: global object
Oof, not what you wanted, right?
why does this happen? Well, you passed "human.getObject" as a callback to "invokeCallback" function, and you handed over the invocation rights to "invokeCallback". Remember, this
is determined by how a function is called, so since "invokeCallback" was the one that invoked the callback function, this
couldn't find any object to refer to so it defaults to the global object.
To fix this, you'd have to explicitly bind this
to "human" object.
function invokeCallback(callback){
return callback();
}
const human = {
name: "Eren Yeager",
getObject() { return this },
};
// explicitly bind `this` to human object
// bind() returns new function
const boundHumanObject = human.getObject.bind(human);
// pass bounded function as callback for "invokeCallback"
invokeCallback(boundHumanObject); // human object
// works perfectly now.
// No matter what calls the function, `this` would always reference "human" object
Some JavaScript inbuilt methods actually allows you to specify the this
value of a callback as an argument. They allow you to pass both the callback and the this
value as arguments. A good example would be the map array method:
const human = { name: "Jackie Chan" };
// callback function that set abilities to any object
function setAbilities(value) {
return `${this.name} can ${value}`;
}
// array with abilities for an object
const abilities = ["eat", "fight", "sleep"];
// Use map() to call "setAbilities" with each item on "abilities" array.
// Pass the "human" object as second argument to map(),
// so that `this` in "setAbilities" refers to the "human" object.
const jackieAbilities = abilities.map(setAbilities, human);
console.log(jackieAbilities);
//outputs: ["Jackie Chan can eat", "Jackie Chan can fight", "Jackie Chan can sleep"]
Other JavaScript in-built methods that accept this
as an argument include:
Array methods - every(), filter(), find(), findIndex(), forEach(), map(), reduce(), reduceRight(), some(), apply(), bind(), call(), match(), replace(), search(), split()
Function methods - call(), apply() and bind()
TypedArray methods - every(), filter(), forEach(), map(), reduce(), reduceRight(), some()
Reflect methods - apply(), construct(), defineProperty(), deleteProperty(), get(), getOwnPropertyDescriptor(), getPrototypeOf(), has(), isExtensible(), ownKeys(), preventExtensions(), set(), setPrototypeOf()
Using this
In Constructors
In simple terms, constructors are object builders; well, life would have been great if that was all it was, so let's dive in.
Constructors are functions used to create new objects with common properties and methods. Apart from the arrow function, any function can serve as a constructor by invoking it with the new
keyword.
this
in constructors would always refer to the new object that the constructor builds when invoked with new
keyword. In a constructor function, this
has no context until the constructor is invoked, at which point it becomes the newly created object.
The this
keyword is a core feature that's needed by constructors to build objects. You can think of how constructors work like this:
function Phone(brand, model) {
// this = {}; (new object created in private by JavaScript)
this.brand = brand;
this.model = model;
// return this; (Javascript returns newly created objects by default.)
}
// When calling the constructor,
// the 'new' keyword creates a new object
const newPhone = new Phone("Samsung", "Note 9");
// The 'new' keyword creates an empty object,
// and set `this` to the object.
// The constructor copies all properties in it to the empty objects
// by using `this` to refer to the new object.
// The new object is then returned from the constructor.
// The new "newPhone" variable now references the new object that was created.
The majority of constructors' names begin with an uppercase letter. It isn't compulsory but just don't be that person.
🚫 Why arrow functions cannot be used as constructors
The reason why arrow functions can't be used as a constructor is because
this
in arrow functions are auto-bounded to thethis
context of its lexical scope when created. This wouldn't work for a constructor sincethis
needs to be bound bynew
keyword to a newly created object which unfortunately arrow functions can't do.
Using this
in class
Classes in JavaScript are like blueprints for building objects.
Developers often refer to them as synthetic syntax for function constructors because they provide a more concise and readable syntax for defining constructor functions.
In this article, we're focusing more on how this
works in classes so going into the nitty gritty details of how constructor works won't be possible (It's quite broad). Take a deep dive into classes here.
Here is a simple declaration of a class compared to a function constructor
// Class Syntax
class Phone {
constructor(brand, model) {
this.brand = brand;
this.model = model;
}
getPhoneName() { return `${this.brand} ${this.model}` }
}
//Function Syntax
function Phone(brand, model) {
this.brand = brand;
this.model = model;
}
Phone.prototype.getPhoneName = function() { return `${this.brand} ${this.model}` };
// In this example, `this` works the same way in both syntax.
// Although, the class syntax is much better to look at.
When dealing with this
in classes, there are two contexts you need to watch out for: Instance and Static. this
has different effects in both contexts. I know, it's a lot.
Instance context
This refers to the properties and methods found in a class that objects created from that class would inherit. In this context, this
refers to the object the class creates.
Constructors, instance methods, and instance fields are all part of the instance context.
Most of the time, you'd use the instance context because they're the ones that contains all what a class needs to create new objects. Now, as complicated as all these may sound, this
is actually pretty straightforward in instance context.
Instance context constructors and fields use this
to refer to the object generated by the class whereas instance method use "this" to refer to the object that called the method (similar to how this
in object method works).
class Game {
constructor(name, type) {
this.name = name;
this.type = type;
}
getDetails() {
console.log(`${this.name} is a ${this.type} game.`);
}
}
const cod = new Game("COD", "shooting");
const streetFighter = new Game("Street Fighter", "fighting");
cod.getDetails(); // COD is a shooting game.
streetFighter.getDetails(); // Street Fighter is a fighting game
this
works as expected. When the class gets invoked with 'new' keyword(very important), a new object is created by the class and all properties found in the class constructor would be made as properties for the new object. Also, all instance methods of the class would be inherited by the new object using the prototype inheritance.
Static context
This refers to the properties and methods that's used only by the class or subclasses. In other words, objects created by classes do not have access to information found in the static context. In static context, this
refers to the class itself.
Static methods, static fields and static initialization blocks are all part of the static context.
The static
keyword is used to declare a method or field as part of the static context. This is a good way to differentiate methods or fields into to their respective context.
class Game {
// instance context
constructor(name, type) {
this.name = name;
this.type = type;
}
getDetails() {
console.log(`${this.name} is a ${this.type} game.`);
}
// static context
static name = "Class for Game objects";
static getName() { return this.name }
}
console.log(Game.getName()); // Class for Game objects
// create an object and try to access a static method
const cod = new Game("COD", "shooting");
console.log(cod.getName()); // error: cod.getName is not a function
In the code snippet above, when the static method getName()
is called, it returns the value of the static property name since this
refers to the class when in static context.
However, when we tried to call getName()
using an object created by the class, it fails because getName()
is a static method, which the new object doesn't have access to.
Keep in mind that objects created by a class cannot access static properties or methods; only the class itself and its subclasses can access them.
Like I said, classes can get pretty wild if you're just starting, but no worries you learn as you go (hopefully).
❕ Important
There's also a popular method that can be found in classes that inherits properties from another class - a method called super().
super()
is a method that is passed to a class constructor that inherits properties from another class constructor.this
refers to the object that called super() which is basically the constructor that's about to create a new object.
Using this
In Event Handlers
Events and events handlers are features in JavaScript that makes developers add interactivity to a website and as expected, this
keyword also made its way there. Fortunately, using this
in event handlers is really straightforward and easy to understand.
Event handlers are functions that are invoked anytime a DOM element trigges an event. The this
keyword in event handlers refer to the DOM element that triggered the event. This makes it incredibly simple for the event handler to access the properties or methods of the precise DOM element that triggered the event.
A simple code example is
//--- HTML Code ---
// <button id="showButton">Hi, I show stuffs</button>
// <button id="hideButton">Hi, I hide stuffs</button>
//--- JavaScript Code ---
const button1 = document.querySelector("#showButton");
const button2 = document.querySelector("#hideButton");
//Event handler function
function handleClick() {
console.log(this.id);
console.log(this.innerHTML);
}
button1.addEventListener("click", handleClick);
// output: showButton
// output: Hi, I show stuffs
button2.addEventListener("click", handleClick);
// output: hideButton
// output: Hi, I hide stuffs
In this code, two event listeners were added to two buttons. The addEventListener is set to call the handleClick
function when a "click" event occurs on any of the buttons. When the event occurs, this
inside the handleClick
function would refer to the button element that triggered the event. The id and innerHTML of the button can then be accessed through this
. Therefore, this
provides an easy way to access information about the specific element that triggered the event.
Best Practices When Using this
Over the years, developers have battled with the fickle nature of this
so as to find out why it doesn't work as expected in their code. So here are some helpful tips and tricks that you can use to rule over the "this" keyword:
this
in functions is determined by how the function is invoked. Alone, this would refer to the global object (non-strict mode) or undefined (strict mode).Be careful when using
this
in arrow functions,this
in arrow functions refers to thethis
context of the arrow function lexical scopethis
in global scope is the global object.this
in object methods refers to the object that the method is called upon, not the object that owns the method.this
in event handlers refers to the DOM element the event handler is attached to.this
is a keyword; you can access what it refers to but you can't assign it a value like how variables do e.gthis = car; // error
Use
bind
,call
, orapply
to explicitly set thethis
value to a desired object of choice. However, keep in mind that these methods cannot changethis
in arrow functions.When using
this
, use strict mode to avoid unexpected behavior.Always use
new
when calling a constructor function, if you don'tthis
would refer to the global object.
Wow, it's been a long journey. Thanks for making it to the end! I'm proud of you. If you enjoyed this article, a like would be much appreciated.
Got any questions? Drop them in the comments section, and I'll do my best to help you out.
And hey, if you have any topics or ideas you'd like me to explore, I'm all ears! I'm always available to chat and see what we can create together. Don't hesitate
Send me a text on Twitter - @vayo_tweet
Send me an email - vayospot@gmail.com
Now go out there and dominate that project. You've got this
!
Top comments (2)
I found this very helpful and easy to understand. Thank you for taking your time to share your knowledge.
its nice explanation...