Dorokhov.codes

03. Classes

Back and forth, hi-bye! I will try to explain the OOP structure in JavaScript within a single article.

Objects

We have such a structure in JS like an object:

const obj = {
    name: "Andrew",
    favoriteColor: "orange",
};

Another pretty example:

var car = {
    name: "Tesla",
    year: 2019,
    openDoor: function() {
        // ...
    }
};

car.openDoor();

In fact, under the hood functions are also objects (they have such properties as name, arguments, etc.):

function hello() {
    console.log("Hey!");
}

hello.tag = "Yes, we've just added a new property to the object!";

console.dir(hello); // Check it!

Constructors

Constructor is a function that creates objects, giving them a set of predefined properties and methods.

In fact, we can use any function as a constructor if we call it with the new keyword. The key difference is that when we call a function without new, it can return any value, but when we call it with new, it always returns an object.

const Car = function(model, year) {
    this.model = model;
    this.year = year;
};

const car = new Car("Tesla X", 2020);

There is a convention to capitalize function-constructor names.

Such a construction always returns an object. So we should define properties and methods inside the function with this. But if we call this function without new keyword, all these properties and methods will be applied to the current object or the Window object, for example.

Here’s a new syntax: we use class keyword. In fact, it’s just syntactic sugar. Under the hood we have the same constructor.

class Point {               // By convention, class names are capitalized.
    constructor(x, y) {     // Constructor function to initialize new instances.
        this.x = x;         // This keyword is the new object being initialized.
        this.y = y;         // Store function arguments as object properties.
    }                       // No return is necessary in constructor functions.

    distance() {                // Method to compute distance from origin to point.
        return Math.sqrt(       // Return the square root of x² + y².
            this.x * this.x +   // this refers to the Point object on which
            this.y * this.y     // the distance method is invoked.
        );
    }
}

// Use the Point() constructor function with "new" to create Point objects
let p = new Point(1, 1);    // The geometric point (1,1).

// Now use a method of the Point object p
p.distance()                // => Math.SQRT2

Prototype

Let’s talk about a prototype. JS prototypes are a mechanism, which simplifies using methods of objects by other objects. All constructors have a prototype property, to which we can add new methods. Any method added to the prototype property will be accessible for all objects, that were created by this constructor.

Car.prototype.startUp = function() {
    // ...
}

When we define a constructor, all its methods and properties will go to the prototype property. So these examples are equal:

class Car {
    startUp: () => {};
}
class Car {}
Car.prototype.startUp = () => {};

So, any function has a prototype object (except arrow functions). If we create a new object using this function (with the new), the object will get access to all the methods and properties of its constructor.

Any object has the __proto__ property. It refers to the prototype object of its constructor. We can even create our own prototype object like this:

const car = {
    color: 'red',
    maxSpeed: 150
};

const bmw = {};
bmw.__proto__ = car;

But it’s an old approach. Here’s a new and correct one:

Object.setPrototypeOf(bmw, car); // Dynamic way.

const bmw = Object.create(car); // At the stage of creating.

Of course, if we have a constructor and create an object using it, we don’t need to manually assign a prototype, as shown in the example above.