Prototypes are a powerful tool, yet pretty low-level. In other words, writing a lot of repetitive code is required when using prototypes, especially when building chains. For this reason, JavaScript developers have introduced the concept of Classes into the language, as it's more familiar and understandable to most developers. Classes have made it really easy to program in JavaScript, but classes in JavaScript are very different from classes in many other languages. Let's start with an example. Below is the code for a regular JavaScript abstraction:
function Company(name, email) {
this.name = name;
this.email = email;
};
Company.prototype.getName = function getName() {
return this.name;
}
Company.prototype.getEmail = function getEmail() {
return this.email;
}
Company.prototype.setEmail = function setEmail(email) {
this.email = email;
}
const company = new Company('Hexlet');
console.log(company.getName()); // => "Hexlet"
This code can be represented as a class:
// Each class must be in its own file
// Ideally, the class name should be the same as the file name, including having the same case
class Company { // the class name is the name of the constructor function
// The method named constructor corresponds to a constructor function
// It's called when we make a new Company(name, email)
constructor(name, email) {
this.name = name;
this.email = email;
}
// This is the getName property with the usual function written inside it
getName() {
return this.name;
}
getEmail() {
return this.email;
}
setEmail(email) {
this.email = email;
}
}
// In terms of usage, nothing changes
const company = new Company('Hexlet', 'support@hexlet.io'); // the constructor method is called
console.log(company.getName()); // => "Hexlet"
The method corresponding to a constructor function within a class is called a constructor
. The interpreter calls it automatically when a new object is created via new
. If there's no constructor, nothing bad will happen. This is equivalent to creating and calling an empty constructor function:
function Company() {
}
const company = new Company();
Classes increase the level of abstraction and give developers a tool they're used to in other languages. They really make it so you don't have to think about the underlying prototypes. At least as long as we're talking about writing application code. But during debugging, you can't do without knowing the prototypes. They're also sometimes used directly in libraries because they allow you to do things that cannot be done with classes.
Properties
There are properties in objects that have default values. For example, a company may have employees. If no employees have been added to the company, the method that returns the list of employees must return an empty array. If it doesn't, it will make development very difficult because it will have to do existence checks everywhere:
const company = new Company();
company.getEmployees(); // []
How to do this? Without classes, this is done right within the constructor function:
function Company() {
this.employees = [];
}
That's what they do with classes:
class Company {
constructor() {
this.employees = [];
}
// other methods
}
However, there is another way. It is popular in other languages but is just gaining it in JavaScript. This method is based on a new syntax (which is in the process of being included in the standard and so far only works through a special plugin called Babel) for defining properties within a class:
class Company {
employees = [];
}
This definition initializes the property for each object individually, as in the examples above. You see this kind of code more and more often nowadays.
Pitfalls
Classes in JavaScript are not static as in many other languages, where once it's defined you cannot change it again. We still work with prototypes and functions that act as constructors. We can write such code after the class has been defined:
// Anywhere in the program after defining the class
Company.prototype.greeting = function greeting() {
return `Hello, ${this.name}!`;
}
const company = new Company('Hexlet');
console.log(company.greeting()); // => "Hello, Hexlet!"
How to use classes correctly?
It's worth starting with the fact that classes are not a necessity in JavaScript. JavaScript is a powerful programming language that doesn't force one programming paradigm (unlike Java, for example). It's easy to do simple things with it and apply complex concepts to complex things when needed.
OOP in general, and classes in particular are difficult ideas that cannot be mastered in advance from courses, videos, and articles. The only way to understand them is to write production code and make and fix mistakes (with advice from more experienced developers).
There is a myth that classes are needed to model the real world. They do do do that (even in the code above we have a company), but this is a very narrow understanding of the purpose of classes. In life, they are used for completely different purposes. There can be hundreds or thousands of classes in the code of complex applications, most of which have no connection with the real world (adapters to the database, different caching strategies). In the next OOP courses, we'll dive deeper into how to design classes and when to use them.
Recommended materials
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.