In this lesson, you will be introduced to classes in JavaScript.
If, for example, in your code you have to work with a lot of points in space, you might want to create a Point class. This class would have an x and a y value. You could then create many instances of this class, each with their own x and y values. The class could also have methods that operate on the x and y values.
Having classes allows you to organize your code in a more object-oriented way. Which can be very useful for certain types of projects.
Object Oriented Programming (OOP)
Object Oriented Programming (OOP) is a paradigm in which most things are typically built out of objects that are instanced from classes defined by the programmer.
The language credited with the invention of OOP is Simula, which was created in the 1960s by Ole-Johan Dahl and Kristen Nygaard. It introduced the concepts of classes, objects, and inheritance.
OOP was a big deal when it was introduced. It allowed programmers to think about their code and structure it in a way that seemed more natural.
However, as with all paradigms, it has its drawbacks. OOP can be very useful for certain applications, but it can also be overused or used badly. It is not a silver bullet for all programming problems.
Since you are writing JavaScript, a multi-paradigm language, you are free to mix and match it with other styles and paradigms as you see fit.
First off all, you'll see how to use a class that has already been defined.
How to Instantiate JavaScript Classes
If you've already got a class definition, the way you instantiate a new object from it is with the new keyword:
const center = new Point(150, 150);
console.log(`The point is at x: ${center.x} and y: ${center.y}`);
Here a new Point object is instanced from the Point class (yet to be written). Then the .x and .y properties are read to log its position.
How to Define JavaScript Classes
Classes are typically named with nouns and are written in PascalCase:
class Point {}
console.log(new Point()); // Point {}
const center = new Point();
center.x = 10;
console.log(center.x); // 10
This is a class declaration at its most basic. It allows you to instantiate empty Point objects. These objects behave like any other object in JavaScript, you can assign values to them arbitrarily, as you can see in the last two lines of the code snippet above.
You can assign values to the new object arbitrarily like this because it is an object, after all. However, you typically don't want to do that, as using classes is about defining specific ways to interact with objects that have specific shapes. It's a way to defend against the chaos of arbitrary objects.
This means that you can have hundreds of Point instances and be certain that they all have an .x and .y attribute, and only those attributes. This is a very powerful concept, and it's one of the reasons why classes are so useful.
The JavaScript Constructor Method
One of the most common things you tend to see on a class declaration is a constructor function:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
A constructor function is a special function that is called when you instantiate a new object from a class. When the new keyword is used to instantiate a class object, the constructor function is called.
When calling the constructor function, you pass arguments like you would to any other function:
const center = new Point(150, 150);
console.log(
`The point is at x: ${center.x} and y: ${center.y}`
); // The point is at x: 150 and y: 150
Note that the constructor() function does not require any special keywords like function before it, all it needs is to be in a class.
The JavaScript this Keyword
When writing a constructor or class method, you can use the this keyword, which refers to the object being instantiated.
this is a peculiar keyword that you'll need to be mindful of, as it changes its behavior based on the context it is in. For example, you can use it in functions, and even in the global scope!
For more information on this, see the MDN documentation article on this. Also, take a look at the lesson on Object Instantiation in JavaScript 101.
Add a Method to a JavaScript Class
Adding a method to a class is similar to defining the constructor, which in turn is like defining a function, except you don't need the function keyword:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
add(point) {
this.x += point.x;
this.y += point.y;
}
}
Here, an add() method is written in the Point class. It takes in another Point instance as an argument (or at least, an object with an .x and .y property), adds that instance's .x to its own .x and does the same with its .y:
const origin = new Point(0, 0);
const offset = new Point(10, 10);
origin.add(offset);
console.log(origin.x, origin.y); // 10, 10
As you can see, this makes it more idiomatic to offset or add points instead of having to operate on x and y coordinates individually.
You'll have also noticed that the this keyword was used in the method to refer to the object that the method was being called on. So in the last code example, this in that context referred to the origin point.
How JavaScript Classes and Objects are Structured
Take as an example the Point class and a few points instantiated from it:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
add(point) {
this.x += point.x;
this.y += point.y;
}
}
const points = [];
for (let i = 0; i < 10; i++) {
points.push(new Point(i * 10, i * 10));
}
First, log just one point to the console:
console.log(points[0]);
/*
Point {x: 0, y: 0}
x: 0
y: 0
[[Prototype]]: Object
add: ƒ add(point)
constructor: class Point
[[Prototype]]: Object
*/
The Role of Prototypes in JavaScript Objects
What is all this business about [[Prototype]]?
Almost all objects in JavaScript have prototypes. In the case of this instance of Point, the specific properties, x and y are connected to the individual instance.
The add() function, however, is linked to the prototype, which is the class definition. The class definition is also a type of object. In the recent examples (and in most use cases), the class object is used as a sort of parent object for all the individual instances.
Understand Object Inheritance
In some languages, the add() method might be duplicated on each instance. Not so in JavaScript! Here, there is just one add() function that lives on the prototype. All Point instances are linked back to the class declaration object, the Prototype object.
Just how Point has a prototype, the class declaration object has a prototype too, the Object prototype, which is one of the fundamental prototypes in JavaScript.
If you examine the Object prototype, you'll see how it has a bunch of methods, one of which is the .toString() method. You can call this method on the Point instance even though you haven't defined it explicitly:
points[0].toString(); // `[object Object]`
The method is not much use to you right now, but it does illustrate how methods are inherited. This is a mechanism you can use if you are designing object hierarchies.
If you want to use the .toString() method to get a string representation of your object, you can define it on your class:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
add(point) {
this.x += point.x;
this.y += point.y;
}
toString() {
return `Point {x: ${this.x}, y: ${this.y}}`;
}
}
This overrides the .toString() method that is inherited from the Object prototype.
You can also see this inheritance by using the instanceof operator:
points[0] instanceof Point; // true
points[0] instanceof Object; // true
As you can see, any object created with the new Point() syntax is an instanceof both the Point prototype and the Object prototype.
Create a Base Class
In this section, you'll create a base class for different shapes. This will allow you to create a base class that can be extended into more specific shapes.
Examine the below example of a hypothetical Polygon class:
class Polygon {
constructor(...args) {
if (!args.every((point) => point instanceof Point)) {
throw new TypeError(
'All arguments must be instances of the Point class'
);
}
this.points = args;
}
getSides() {
return this.points.length;
}
}
In this snippet you:
- Start a class declaration for the
Polygonclass - The constructor takes in a variable amount of arguments with the rest parameter syntax, into a variable called
args - Check each item of
argsto make sure they are all instances of thePointclass using the.every()array method - If all the
argsare points, then they are set as the object's.pointsproperty
Inherit From a Class and the super Keyword
Now that you have a general Polygon class, you can extend this base into more specific shapes. You could define a shape that has four sides, for instance:
class Quadrilateral extends Polygon {
constructor(...args) {
if (args.length != 4)
throw new TypeError('Quadrilateral must have exactly 4 points');
super(...args);
}
}
This code:
- Starts a new class declaration for
Quadrilateral, using theextendskeyword that sets up this class as a child ofPolygon - Sets up a constructor function with the same
argswith the rest parameter syntax - Checks to ensure that the
argsare exactly four in number, because it is a quadrilateral, after all - Uses the
superkeyword to call the parent's constructor.
The super keyword is the child class' way to access the parent class. It is used as a function to call the parent's constructor. Calling super() also unlocks the this keyword for the child class.
Additionally, if you have a method on the parent class that you want to call from the child class, you can use the super keyword to do that too.
What is the typeof a JavaScript Class?
Taking the Point class declaration, using the typeof operator on the class gives a mysterious result:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
console.log(typeof Point); // 'function'
What's all this about a function? Isn't it meant to be a class?
Special Functions
Something that makes JavaScript classes quite different from other languages is that classes are defined as "special functions".
This means that you can write the same code as above by defining a function:
function Point(x, y) {
this.x = x;
this.y = y;
}
const center = new Point(150, 150);
console.log(center); // Point {x: 150, y: 150}
Here you define a function and then use the new syntax to create a new object instance.
To define a method on a class defined as a function, you would need to define a property on Point.prototype as a function:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.add = function (point) {
this.x += point.x;
this.y += point.y;
};
const center = new Point(150, 150);
const offset = new Point(10, 10);
center.add(offset);
console.log(center); // Point {x: 160, y: 160}
Here you add a method to the Point.prototype, which can then be used on instances of Point.
So, classes in JavaScript are really just special functions. There are many advantages to using the class syntax, however, so don't think that it's purely syntactic sugar. There are extras like private field declarations, which aren't very easy to implement without the new class syntax.
Beware of Arrow Functions for Methods
When adding methods to a class, you should beware of using arrow functions:
// ...
Point.prototype.add = (point) {
this.x += point.x
this.y += point.y
}
const center = new Point(150, 150)
const offset = new Point(10,10)
center.add(offset)
console.log(center)// Point {x: 150, y: 150}
// No change to the original point
This is because arrow functions use the this value of the enclosing scope. Which in this case is the global this, the Window:
Summary: Introduction to JavaScript Classes
In this lesson you've covered these concepts:
- Classes in JavaScript provide a blueprint for creating objects with specific properties and methods.
- Instantiation in JavaScript allows you to create new instances from a class using the
newkeyword. - The constructor method is a special method in classes where you define the initial state of the object.
- Utilizing the
thiskeyword within constructor and class methods refers to the current instance of the class. - Inheritance and the
superkeyword enable subclasses to derive properties and methods from a parent class, allowing for hierarchical designs. - Surprisingly, despite the class syntax, JavaScript classes are essentially special functions under the hood.