Composition in JavaScript

Composition in JavaScript

Alright, first things first. Before understanding composition in JavaScript, we need to understand some basic terms related to Object-oriented programming. We all know, we can achieve the functionality of OOP in any programming language by making classes in it. After making classes, we can initialize some objects from it which will be having all the functionality defined in that class. A very basic example is given below:-

class Animal{
    constructor(name){
        this.name = name;
    }

    eat(){
        console.log(`${this.name} is eating.`);
    }

    drink(){
        console.log(`${this.name} is drinking.`);
    }
}

const tom = new Animal('Tom');
tom.eat();

and, this will give us the expected output:- Tom is eating. Pretty easy till now.

Now, another interesting thing that we can do in Object-oriented programming is Inheritance. Inheritance means that we can inherit the functionality (methods) of one class in some another class ( and we can also new methods into it) so as to create an IS-A relationship.

Let me give an example of this too so as to make sure we are on the same platform.

Consider the code written above, the Animal Class and we are going to add some new code below that:-

class Dog extends Animal{
    bark(){
        console.log(`${this.name} is barking.`);
    }
    walk(){
        console.log(`${this.name} is walking.`);
    }
}

Now, what this piece of code is doing is that it makes a new class named Dog and it is inheriting all the methods of the Animal class. This implies the IS-A relationship such that Dog IS-A (an) kind of Animal and therefore it will inherit all the features from it but also it will have some unique features of it which we can define in Dog class itself and now if we create a new object from the Dog class, we can call all the methods on it whether from Dog class or from Animal class (you can try it on your own).

Composition starting from here !!!

Now, let's see some problems with Inheritance, due to which I am writing this article ( so as to make you understand Composition ). Let's say that we want to make a new class for Alligators and for those who don't know about alligators, they can walk as well as swim. So, here's the code for an Alligator class.

class Alligator extends Animal{
    swim(){
        console.log(`${this.name} is swimming.`);
    }
    walk(){
        console.log(`${this.name} is walking.`);
    }
}

but, as you can probably see that the walk function is also in Dog class, so basically we are repeating our code which is not a good practice. Now most of you are probably thinking that this is just 1 line of code or we can do something to remove it like adding the walk function in Animal class, but remember this is not correct for obvious reasons like not all animals can walk and this is just a small example. In building real-life projects, things can go really messy with this.

So finally, let's talk about Composition which is a way to tackle this problem with Inheritance. The basic thing with composition is that instead of using the IS-A relationship it uses the HAS-A relationship ( which will be made clear after an example ) or we can say that, instead of inheriting the properties and/or functionalities (methods) from other classes in case of writing a complex class we can make small components and take those components to make an object which has all the functionalities from all of them, hence the name composition.

Now, let's take a look at an example. Instead of writing the previous code that is mentioned in this article, write this code:-

function swim({ name }){
    return{
        swimming: ()=>console.log(`${name} is swimming.`)
    }
}

function walk({ name }){
    return{
        walking: ()=>console.log(`${name} is walking.`)
    }
}

and if we create an object now from the same Animal class we wrote at the very beginning, we can add these methods to it whenever we need that functionality just like this:-

let alligator = new Animal('Alligator');
alligator.eat();
alligator.drink();
alligator = swim({name: 'Alligator'});
alligator.swimming();
alligator = walk({name: 'Alligator'});
alligator.walking();

so, in this way, we can add multiple methods to our alligator object which simply shows that alligator HAS-A (an) ability to swim as well as to walk which is not the case with all the animals and that's how composition actually works.

Also, we can take this one step even further by making a new function which returns both swim and walk function just like that:-

function amphibians(name){
    const amphibian = {name};
    return{
        ...walk(amphibian),
        ...swim(amphibian)
    }
}
const frog = amphibians('Frog');
frog.walking();
frog.swimming();

I hope you understand the destructuring of an object.

Hence, we created the functionality of the class inheritance by only functions ( which are the components ) for a certain object.

“I will also soon be publishing it on GeeksforGeeks