You don't need classes

A few years back, JavaScript added a long-awaited feature to the language: Classes. Developers coming from other languages with the classic object-oriented paradigm were happy to find their old friend in JS, even if that old friend just behaves mainly as syntax sugar. So first, let's take a look at how classes work, and then we can tackle why you don't need them.

Sugar, oh honey honey

Let's just write the classic Shape class:

class Shape {
	constructor({ name = "shape", x, y }) {
		this.name = name;
		this.x = x;
		this.y = y;
	}
	move({ x, y }) {
		this.x = x;
		this.y = y;
	}
}

class Circle extends Shape {
	constructor({ name = "circle", x, y, radius }) {
		super({ name, x, y });
		this.radius = radius;
	}
}

const circle = new Circle({ x: 10, y: 10, radius: 5 });
circle.move({ x: 20, y: 20 });

This is, in practice, just syntax sugar for functions and prototypes:

function Shape({ name = "shape", x, y }) {
	this.name = name;
	this.x = x;
	this.y = y;
}

Shape.prototype.move = function ({ x, y }) {
	this.x = x;
	this.y = y;
};

function Circle({ name = "circle", x, y, radius }) {
	Shape.call(this, { name, x, y });
	this.radius = radius;
}

Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;

const circle = new Circle({ x: 10, y: 10, radius: 5 });
circle.move({ x: 20, y: 20 });

When you're using classes, you're already using functions, just with extra complexity on top. This might sound great for someone coming from an object-oriented language, but if you think about it, now you need to deal with this, you need to use new when creating a new instance of that class, and so on.

Clean instead of classy

Instead of trying to think everything as a class first, try to think about stuff as just values and process. So the same Shape example can be something like this:

const createShape = ({ name = "shape", x, y }) => ({ name, x, y });
const moveShape =
	({ x, y }) =>
	shape => ({ ...shape, x, y });
const createCircle = ({ radius, ...props }) => ({
	...createShape({ name: "circle", ...props }),
	radius
});

const circle = createCircle({ x: 10, y: 10, radius: 5 });
moveShape({ x: 20, y: 20 })(circle);

With this approach we have a few advantages over the classes one:

  1. We don't have to think about this, because we don't use it at all.
  2. We don't need to use new, we just call functions that return values.
  3. We don't worry about mutations, because we never change values, we just take a value and return a new one. This is quite important when testing because the state becomes predictable.

Do you need classes?

Now think about it for a minute: Do you need classes at all, or are you just used to them? Before working in WebDev, I was a fan of C++, so naturally, I loved classes, but as time went by I realize that every problem that I used to solve with a class, has a cleaner and simpler solution just using functions.

Take a look at your code in the places you used classes, and try to think how you'll do that with just functions, and tell me if that solution isn't just better.

Either way, thanks for reading this and if you don't agree with something said here, just leave a comment and we can discuss it further.

See you in the next post of this series!

Disclaimer

This series is called "You don't need ...", emphasis on need, meaning that you would be fine without the thing that the post covers. This series explores alternatives, it doesn't impose them, so consider that before glancing over the post and ranting on the comment section. Keep it respectful.

Leave a comment