JS Inheritance - Part 2: Factory Functions vs. Classes
Introduction
Last time, we took a look at classes and prototypes in JavaScript. We compared constructor functions to classes and discussed how the class
keyword introduced in ES6 aimed to simplify object-oriented programming in JavaScript. However, the introduction of classes did not solve all the problems associated with constructor functions. Today, we will explore different ways to declare an object in JavaScript and see if there is a need to use classes at all.
Different Ways to Declare an Object
Class
class ClassCar {
drive() {
console.log('Vroom!');
}
}
console.log(typeof ClassCar); // function
const car1 = new ClassCar();
console.log(car1.drive());
Constructor Function
function ConstructorCar() {}
ConstructorCar.prototype.drive = function () {
console.log('Vroom!');
};
console.log(typeof ConstructorCar); // function
const car2 = new ConstructorCar();
console.log(car2.drive());
Factory Function
const proto = {
drive() {
console.log('Vroom!');
},
};
const factoryCar = () => Object.create(proto);
console.log(typeof proto); // object
console.log(typeof factoryCar); // function
const car3 = factoryCar();
console.log(car3.drive());
Each of these strategies stores methods on a shared prototype and optionally supports private data via constructor function closures. In other words, they have mostly the same features and could mostly be used interchangeably. The question now is: you can, but should you?
Factory Functions
We talked about constructor functions. We know that the new
keyword is something that changes a regular function into a constructor function. If we want to free ourselves from the confines of the classical model, we have to embrace the prototypal model. In a prototypal model, objects inherit from objects. (Do you remember Object.prototype
?) However, JavaScript lacks an operator that is responsible for such an operation. Instead, it has a new
keyword that can produce a new object that inherits from Object.prototype
. This was done on purpose to make it look familiar to classically trained programmers, but it failed miserably. It simply did not appeal to the classical crowd. Also, it obscured JavaScript from its real inheritance model.
Should You Use Factory Functions Instead of Classes?
Much like Array
, class
is not a language feature; it’s syntactic obscurantism. It tries to hide the prototypical inheritance model and the clumsy idioms that come with it, and it implies that JavaScript is doing something that it is not. So, by design, there are no classes. However, JavaScript has all the necessary features to implement OOP; there was no need to add classes in ES6. It was added just so Java or C# developers can feel comfortable.
To put a fine point on that, a child of a prototype isn’t a copy of its prototype, nor is it an object with the same shape as its prototype. The child has a living reference to the prototype, and any prototype property that doesn’t exist on the child is a one-way reference to a property of the same name on the prototype.
Most of the time, classes in JavaScript don’t serve a good purpose; they are not really useful.
Why We Should Not Use Classes
Much like Array
, class
is not a language feature; it’s syntactic obscurantism. It tries to hide the prototypical inheritance model and the clumsy idioms that come with it, and it implies that JavaScript is doing something that it is not. So, by design, there are no classes. However, JavaScript has all the necessary features to implement OOP; there was no need to add classes in ES6. It was added just so Java or C# developers can feel comfortable.
To put a fine point on that, a child of a prototype isn’t a copy of its prototype, nor is it an object with the same shape as its prototype. The child has a living reference to the prototype, and any prototype property that doesn’t exist on the child is a one-way reference to a property of the same name on the prototype.
Most of the time, classes in JavaScript don’t serve a good purpose; they are not really useful.
Functional Programming for Life
In JavaScript, functions are first-class citizens. Functional programming is all about using functions to their fullest extent. Functional programming has come and gone and come back again. A couple of reasons why, in my humble opinion, we should try to understand the benefits of functional programming. One should just ask himself if factories are much more flexible than either constructor functions or classes, and they don’t lead people down the wrong path by tempting them with the extends
keyword and deep inheritance hierarchies. There are many safer code reuse mechanisms you should favor over class inheritance, including functions and modules.
Some Code to Prove It
So we could start with something like this:
// class
class ClassCar {
drive() {
console.log('Vroom!');
}
}
console.log(typeof ClassCar); // function
const car1 = new ClassCar();
console.log(car1.drive());
And a little refactor using modules:
// Car.js
export function drive() {
console.log('Vroom!');
}
export function stop() {
console.log('Stopping');
}
// app.js
import * as car from './Car';
car.drive();
car.stop();
So we got rid of two keywords here: new
and class
. As we see in the above example, we do not really need them to achieve what we want to achieve. Another example would be:
class Child extends Parent {}
// instead we could do
const fun = parent(child());
Here we also saw another keyword extends
that has come and gone. Do we really need it? Answer: not really.
A real-life example utilizing such a programming style would be the Fastify framework for Node.js backend development.
Moreover, on the frontend, React has also ditched the old concept of React.Component
and has moved towards functional components and hooks.
Summary
Classes in JavaScript make things look more familiar to classically trained developers. However, it is only a sugar coating, and when uncovered, it reveals JavaScript's true nature. And the true nature of JavaScript is prototypical inheritance. So, if you are not one of the developers coming from OOP programming languages like Java or C#, then I strongly suggest stopping using classes and favoring objects and functions, and moving on to modules. This will be extremely beneficial if you want to work in frameworks like React or Fastify. JavaScript, like any other language, has good and bad parts. Surely, one of the best parts is the lack of classes and class inheritance. It might take some effort and time to truly master prototypal inheritance, but it is surely worth it.
Top comments (0)