DEV Community

Cover image for Understanding Dependency Injection in JavaScript
Mohan Murali
Mohan Murali

Posted on • Edited on

Understanding Dependency Injection in JavaScript

Dependency Injection is a fairly complex topic for beginners. It might not be necessary to know the concept but knowing it will help you make better decisions on your code.

Lets start by the definition.
Dependency Injection - Dependency Injection is the technique in which an object receives other object in which it depends on (source: Wikipedia).

Now lets try to break down the definition a bit. Lets start by object. An object is an instance of a class. For Example

// lets define a class dog
class Dog{
  speak(){
    console.log("wuff");
  }
}

//now lets create object dog
const fluffy = new Dog();
Enter fullscreen mode Exit fullscreen mode

In the above example, we have a class Dog and fluffy is the object of the class Dog. When we new up a class we create an object of that class. This is one of the ways of creating an object in Javascript (and its the common way to create an object in languages like c# and java).
Lets now see an example where 2 objects are dependent on each other.

class Pet{
  whatDoesMyPetSay(){
    const pet = new Dog();
    pet.speak();
  }
}

const fluffy = new Pet();
fluffy.whatDoesMyPetSay();
// response will be "wuff"
Enter fullscreen mode Exit fullscreen mode

Here, as we see, the class Pet is dependent on class Dog. So to get what we want, we need to create an instance of Dog inside our Pet class. Now this class is not reusable as it is tied to Dog class. If someone has a cat as Pet, they wont be able to use this class. This is what is called as tightly coupled code.
Now lets change this code and try to satisfy all other pet owners with dependency Injection. But first, lets create a cat class

class Cat{
  speak(){
    console.log("meow");
  }
}
Enter fullscreen mode Exit fullscreen mode

The cat class must also implement the same method for Dependency Injection to work. In languages like C# and Java this is ensured by using an interface. But we have no such method in JavaScript, so it is up to the developer to remember it. Now lets see the new implementation of the pet class.

class Pet{
  //usually we have a private variable that needs 
  //to be accessed only in this class
  #pet;

  //create a constructor that recieves the dependent
  //object
  constructor(pet){
    this.#pet = pet;
  }

  whatDoesMyPetSay(){
    //as long as pet class implements speak method we are fine
    this.#pet.speak();
  }
}

//what does fluffy the dog say?
const fluffy = new Pet(new Dog());
fluffy.whatDoesMyPetSay();
//The response will be "wuff"

//what does milo the cat say?
const milo = new Pet(new Cat());
milo.whatDoesMyPetSay();
//The response will be "meow"
Enter fullscreen mode Exit fullscreen mode

Now, we have removed the dependency from inside the pet class and have given it to the caller of the class. This promotes the reusability of the pet class. This is a very simple example and the purpose is to only understand dependency injection and not to implement it. In real world, the dependency is abstracted even from the caller and given to a new object, which is usually called an Injector.

Why not to use Dependency Injection in JavaScript

If you have read till here I hope you are clearer with the concept of dependency injection. Now lets see some reasons we might not want to use dependency injection

  • Unlike purely class based languages like c# and java, JavaScript provides us lots of flexibility when it comes to grouping functionalities. Many times, you won't even need to use a class and just using a function might be enough. In those cases, trying to implement Dependency injection will only create unnecessary complexities.
  • JavaScript also happens to be dynamic in nature. You can overwrite and use any function implementation in JavaScript. So we should make use of these rather then dependency injection for unit testing our code.

At the end of the day, as a developer, we should realize that there is no one solution that fits all problems. I hope with this article, you would be able to take a better decision on your solutions.

Top comments (17)

Collapse
 
silviumihnea profile image
Silviu-Mihnea Cucuiet

What you describe is not dependency injection, but dependency inversion. You made it so the class Pet does not automatically create a dependency, but rather takes it as a parameter. I think an important bit that you left out is the fact that the instance of the Dog class(fluffy) can be reused in other classes. Good point to specify a purpose for dependency inversion is to create more instances of the dependant class with different dependencies. The bit about the injector is right, dependency injection is a pattern where you don't even need to create any of your services / components / dependencies, but rather let a framework create them for you and figure out the order of the creation and injection of the dependencies. I wish you detailed more on that part.

Collapse
 
_mohanmurali profile image
Mohan Murali

My aim was to make people understand the concept of loose coupling through dependency injection. This is a beginner focused post and don't want to confuse people with information they might not need. Thanks for reading.

Collapse
 
iliashterevgit profile image
ilia-shterev-git

Without being an expert I think that dependency injection is one form of practical application of the dependency inversion.

Collapse
 
jeel profile image
JP

right, agree

Collapse
 
jurijzahn8019 profile image
Jurij Zahn • Edited

I agree with the Author. Js is highly dynamic and powerful so I think it does not really need any kind of IOC. If I pick up the example I will probably end up with something like this:

function whatDoesMyPetSay < T extends { speak: () => string; }>(pet: T): string {
pet.speak();
}

🙂

Collapse
 
_mohanmurali profile image
Mohan Murali

Yes, I agree, You can do it in many ways in JavaScript.

Collapse
 
tanth1993 profile image
tanth1993

many thanks. I've read about "Dependency inversion principle" and I know it clearly after reading your article.
Even though JS doesn't have interface, this is a good, clear and simple example.
in JS files, I usually use export and import some functions into a module, so is it the way could replace DI?

Collapse
 
_mohanmurali profile image
Mohan Murali

Yes, you dont really need to create class objects to use funtions in js. You can directly use functions in most of the case. Glad you liked it.

Collapse
 
essayscambjohn profile image
John Miller

JavaScript also happens to be dynamic in nature.

Collapse
 
ashishnimrot profile image
ashishnimrot

Great

Collapse
 
_mohanmurali profile image
Mohan Murali

Thanks for reading.

Collapse
 
rhodes235 profile image
Sathya

Good read

Collapse
 
_mohanmurali profile image
Mohan Murali

Thanks for reading

Collapse
 
cherif_b profile image
Cherif Bouchelaghem

Dependency injection is passing dependencies as parameters, js supports higher-order functions, so why just don't use partial application and pass functions as dependencies to other functions?

Collapse
 
_mohanmurali profile image
Mohan Murali

Yes, that will indeed be the most optimal way in javascript or any other functional programming language. But DI is very Object oriented concept. If you are working with Class Objects you might be need to use DI. Its all based on situation of use.

Collapse
 
amitabhlaksh profile image
Amitabh Suman

Very nice and easy explanation of not so easy concept. Thanks 👍🏻

Collapse
 
_mohanmurali profile image
Mohan Murali

Thanks, glad you liked it :)