Explain Codes LogoExplain Codes Logo

What techniques can be used to define a class in JavaScript and their trade-offs?

javascript
object-oriented-programming
prototype-based-inheritance
closures
Anton ShumikhinbyAnton Shumikhin·Oct 3, 2024
TLDR
class Shape { constructor(color) { this.color = color; } draw() { console.log('Drawing a shape'); } } const etchASketch = new Shape('red'); // Nothing draws better than a good ol' etch-a-sketch etchASketch.draw();

Utilize the class keyword in JavaScript for creating a class to achieve simplicity and readability. It provides a familiar structure for those accustomed to classical Object-Oriented Programming (OOP). Though it is just syntactic sugar over prototypical inheritance, it streamlines object creation and prototype chain management, easing the process of understanding and maintaining code. Yet, this approach is a bit restrictive when compared to the dynamic nature inherent in prototype-based patterns.

Playing with prototypes

function Animal(name) { this.name = name; } Animal.prototype.speak = function() { console.log(this.name + ', says mooooo...'); } var cow = new Animal("Cow"); cow.speak(); // Cow says mooooo...

JavaScript leverages prototype-based inheritance, a distinct feature separating it from traditional OO languages. It allows for the dynamic amendment of class prototypes, even at runtime — very empowering, but also a Pandora's box if not handled properly.

Closures for privacy

function Car(speedLimit) { let _currentSpeed = 0; this.speedLimit = speedLimit; // No more speed freaks! this.accelerate = function() { if (_currentSpeed < speedLimit) { _currentSpeed++; } console.log('Current speed: ' + _currentSpeed + 'mph'); }; } let vehicle = new Car(60); vehicle.accelerate();

Closures provide a mechanism to encapsulate properties, serving as our personal VIP lounge for data. However, it's a bit memory-hogging as each instance receives its functions.

Drawing inspiration from libraries

// How Prototype extends a class var Emu = Class.create(Bird, { initialize: function($super, name) { $super(name); // Calls the initialize method from Bird class }, run: function() { // Emus can't fly, but boy can they run! console.log(this.name + ' starts running at incredible speed!'); } });

Notable libraries such as Prototype and jQuery offer utilities to mimic class functionality and offer insights into practical use-cases.

Meet the ES2015 class

const privateData = new WeakMap(); class Car { constructor(maxSpeed) { privateData.set(this, { speed: 0 }); this.maxSpeed = maxSpeed; } // Vrooom! Vroooom! But only till max speed accelerate() { if (privateData.get(this).speed < this.maxSpeed) { privateData.get(this).speed++; } console.log('Current speed: ' + privateData.get(this).speed + 'mph'); } } let bugatti = new Car(304); bugatti.accelerate();

Introduced in ES2015, the class keyword assigns a clean syntax for implementing classes and expandable for future ECMAScript updates. However, for broad browser support, we rely on transpilation tools like Babel.

Beyond classes

const createRobot = (name) => { return { name, speak: function() { console.log(this.name + ": Beep bop beep!") } } } let robot = createRobot("R2D2"); robot.speak(); // R2D2: Beep bop beep!

We can also use methods such as factory (fans of the conveyor belt, rejoice!) and module patterns, along with object composition, to manage object creation. These offer various benefits but often feel alien to those with a classical OOP background.