Ace Your Next JavaScript Interview: `this`, `new`, Prototypes, Classes (Part 3) ✨
Learn the deeper concepts in JavaScript, such as `this`, `new`, Prototypes, and Classes (7 minutes)
This is part 3 of the “Ace Your Next JavaScript Interview” series where I cover the fundamentals of JavaScript, sharing my notes which I use to review and prepare for interviews.
In today’s article, I’ll cover other JavaScript concepts, such as `this`, `new`, Prototypes, and Classes.
If you missed the previous articles, you can check part 1 and part 2:
Outlier (powered by Scale AI) - Sponsor
Outlier provides premium freelance coding opportunities for frontend developers.
up to $50 per hour (USD) based on expertise and location
some other countries it is up to $30 per hour (USD) due to location
weekly payouts via PayPal & AirTM—no waiting weeks to get paid
performance bonuses available on select projects
Explore how frontend developers are shaping AI's future (and getting paid for it).
The `this` Keyword
In object-oriented languages like C# or Java, the `this` keyword refers to the current object of a method or a constructor.
So each time you use `this` inside a method of a object, it will always refer to the same object.
However, in JavaScript, the `this` keyword implementation is slightly different, which sometimes causes many problems and bugs, if not understood well.
Let explore some examples.
Example 1: Method Call
const hero = {
name: 'Aria',
greet() {
console.log(`${this.name} says hi`);
}
};
hero.greet(); // Aria says hi
In this case, everything works as expected.
However, the value of `this` is not determined by the function that uses it.
Example 2: Detached Method
const { greet } = hero;
greet(); // says hi
In this case, we get an empty value for `this.name` because the value of `this` depends on the call-site.
It doesn’t refer to the function in which it’s used or to it’s scope.
It refers to the object on which the function is being executed.
In the example above, I’ve run the code inside the browser, where `this` refers to the `window` object and it doesn’t have a `name` property.
💡 Pro tip: Always ask: “What object sits on the left side of the dot when the function is being called?”
We can say that `this` is not an author-time binding but a runtime binding.
It is contextual based on the conditions of the function’s invocation.
Apart from the mentioned example from above, there’re some other ways of how to manipulate the value of `this`.
Let’s explore them as well just for completeness.
Example 3: Arrow Functions
Arrow functions don’t get their own this.
Instead, they capture it from the surrounding scope.
const knight = {
name: 'Lancelot',
salute: () => console.log(this.name)
};
knight.salute(); // undefined
Example 4: Explicit Binding
We can explicitly set the value of `this` when we call a method.
This is called `binding` because we bind a specific value to `this`.
function introduce() {
console.log(`I am ${this.name}`);
}
const elf = { name: 'Legolas' };
introduce.call(elf); // I am Legolas
As you see, in JavaScript, we can use `this` inside functions, not only classes.
The `new` Keyword
In object-oriented languages like C# or Java, the `new` keyword is used to create a new instance of class.
However, in JavaScript, the behavior of the `new` keyword is slightly different, no matter there’re classes in the language.
In OO languages, using the `new` keyword will result in calling the constructor method of the class.
In JavaScript, we can simply use a function to create a new object.
function Hero(name) {
this.name = name;
}
const hero = new Hero("Gandalf");
console.log(hero.name); // "Gandalf"
When we use the `new` keyword before a function, it will create a new object and bind the `this` inside the function to the newly created object.
Then, it will return the new object, if the function doesn’t return anything.
If the function returns an object, the above is not valid.
function Person(name) {
this.name = name;
return { nickname: `The ${this.name}` };
}
const p = new Person("T-Shaped Dev");
console.log(p.nickname); // "The T-Shaped Dev"
console.log(p.name); // undefined
Note: Arrow functions can’t be used as constructors. They throw if you use `new`.
Note On Using `this` And `new`
If you don’t want the headache of using `this` and `new`, you might prefer a more functional approach.
You can use closures to deal with `this` and factory functions to deal with `new`.
For instance:
// Functional factory + Closure approach
function createCounter(start = 0) {
let count = start;
function increment() {
count += 1;
console.log(count);
}
function reset() {
count = 0;
console.log(count);
}
return { increment, reset };
}
const c2 = createCounter(5);
c2.increment(); // 6
c2.reset(); // 0
Prototypes
Objects in JavaScript have a `prototype` property which references another object.
So in JavaScript, we can build an inheritance object hierarchy through using Prototypes.
The other way of building such hierarchies is through Classes which are a syntax sugar over Prototypes.
Whenever you read a property of an object, it will first try to find it in the object itself.
If not found inside the object, it will look for it into the `prototype`.
If it’s not found there as well, it will check the prototype
’s prototype
and so on until it reaches the Object.prototype
.
const hero = {
salute: () => console.log('Salute!')
};
const knight = {
name: 'Knight',
__proto__: hero
};
knight.salute();
In this case, we set the `__proto__` property explicitly for the specific object.
To set up the inheritance chain, we can do it in the constructor function so every created object has the correct prototype.
function Hero(name) {
this.name = name;
this.salute = function () {
console.log(`${this.name}, Salute!`);
}
}
function Knight(name) {
this.name = name;
}
// create a reference for the prototype
Knight.prototype = new Hero();
const knight = new Knight("Lancelot");
knight.salute(); // Lancelot, Salute!
By setting the `prototype` object to the constructor function, we ensure that all objects created through the `new` keyword will set up properly.
Note: There are some other quirks around Prototypes in JavaScript, but I’ve found the shared notes above to be fundamental.
Classes
ES6 introduced Classes which are a syntax sugar over Prototypes.
Nowadays, using Prototypes is discouraged because of the high complexity around setting up big inheritance hierarchies.
It’s most common to use Classes also because of the familiarity of people with other OO languages.
class Hero {
constructor(name) {
this.name = name;
this.salute = function () {
console.log(`${this.name}, Salute!`);
};
}
}
class Knight extends Hero {
constructor(name) {
super(name)
}
}
const knight = new Knight("Lancelot");
knight.salute(); // Lancelot, Salute!
Composition
Inheritance is a powerful tool to extend entities and create more complex object but it’s not ideal for all use cases.
Creating complex inheritance hierarchies is not trivial and most of the time we might end up with unnecessary complexity.
Personally, I think that the best way to create complex objects in JavaScript is through composition.
This way, we create objects with only what they need, they combine them to create a more complex ones.
function canAttack(state) {
return {
attack: () => console.log(`${state.name} slashes!`)
};
}
function canHeal(state) {
return {
heal: () => console.log(`${state.name} heals!`)
};
}
function createPaladin(name) {
const state = { name };
return {
...canAttack(state),
...canHeal(state),
name
};
}
const paladin = createPaladin('Theron');
paladin.attack(); // Theron slashes!
paladin.heal(); // Theron heals!
Performance Considerations About Inheritance
It’s important to mention that if performance is critical to the app then defining a function to the prototype might be a better option, instead of creating a fresh new copy every time.
This means that the function will be created once and will leave on the shared prototype.
function FastFighter(name) {
this.name = name;
// per-instance method
this.attack = () => console.log(`${this.name} strikes fast!`);
}
// vs.
FastFighter.prototype.attack = function() {
console.log(`${this.name} strikes fast!`);
};
This can be helpful in some scenarios but might lead to problems if not used properly because we deal with references, instead of actual copies.
📌 TL;DR
`this` is set by the call-site, not where a function lives.
`new` creates a fresh new object, binds `this`, and returns it.
Prototypes forms a chain, so objects can inherit missing properties.
Classes are a syntax sugar over Prototypes.
Prefer composition to mix small behaviors, instead of deep and large hierarchies.
We can share code through using prototype methods, boosting performance.
That's all for today. I hope this was helpful. ✌️
How I can help you further?
Become a better and more skilled React Engineer! 🚀
👋 Let’s connect
You can find me on LinkedIn, Twitter(X), Bluesky, or Threads.
I share daily practical tips to level up your skills and become a better engineer.
Thank you for being a great supporter, reader, and for your help in growing to 26.3K+ subscribers this week 🙏
You can also hit the like ❤️ button at the bottom to help support me or share this with a friend. It helps me a lot! 🙏