Why You Have to Use Dependency Injection in JavaScript or Not
Dependency injection solves a problem in statically typed, object-oriented languages that would not exist if they were no static type system. The concept is not a value being implemented at any cost. As is so often the case, it is better to first become familiar with the JavaScript language and then uses it as it suits you — instead of adopting the patterns known from C# or other languages more or less one-to-one in the expectation that both worlds would work in the same way.
Arnold Abraham

The moment has come, and you have thought about decoupling components in JavaScript and also Fry is observing you suspiciously.
Decoupling components is a mighty weapon against cluttered code bases and breaking changes. Load this weapon with a magazine of Dependency Injection (DI).
Therefore, DI enables you the replacement of dependencies without having to change the class that uses them. It can also reduce the risk for a change of a class just because one of its dependencies changed.
JavaScript and the Tale of Dependency Injection
Changing components means having an interface that specifies the interaction contract between two components.
Having interfaces means creating objects inferring from the interface.
Rely on interfaces instead of concrete classes and you get interchangeability since both components must fulfill the interface’s contract.
That means DI bases on decoupling concrete classes from dependencies by relying on contracts (interfaces) and not on concrete classes.
The key to success is to ask for contracts (interfaces) without knowing the concrete type to get the interchangeability we are searching for.
To make this possible, the language you are using has to support a powerful type system and lets you use interfaces.
Does JavaScript have interfaces? Does JavaScript have a static type system?
OOP Languages Do That, but JavaScript?
Since ECMAScript 2015 are two keywords usable: class
and extends
. But both of them aren’t a thing compared to real object orientation.
Both requirements to support DI are half-baked in JavaScript.
It is more like syntactical sugar or magic to hide the JavaScript prototype-based inheritance.
Even if you are going to count this into object orientation, JS got no interfaces at all.
This leads to the conclusion of not being able to do an object-oriented Dependency Injection in JavaScript, doesn’t it?
The Solution Is Already There
If we aren’t able to use the real Dependency Injection in JavaScript, we should consider the background of DI and what it solves.
Dependency Injection solves a problem of object-oriented languages that just only exists because of the usage of a static type system.
JavaScript uses the dynamic type system.
Dependency Injection normally builds the fundament for the interchangeability of classes and is going to depict this via interfaces.
That means every passed object to a method has to fulfill the contract of the interface, otherwise, the program would crash.
Interfaces ensure this contract and the completeness of an object.
Passing an object into a function in JavaScript and calling a function of that object that does not exist, but should exist, won’t crash your application.
To go even further, JavaScript does not care about the other functionality of the object being passed in. As long as fulfilling the required functionality.
And we call this duck typing.
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. — James Whitcomb Rileys
Since this isn’t a type of manner in JavaScript, they transformed heavily the quote:
When I see a bird that quacks like a duck looks like a dog and walks like a cat, I call that JavaScript. — Common Saying
So you simply pass a matching object to a component that needs, for example, a logger and it doesn’t matter if it formally matches the expected contract as long as it matches the task.
Since objects in JavaScript have no type apart from objects they consequently cannot match.
Of course, this leaves open the question of how to configure the thing — how to implement in JavaScript the task that would be solved with a DI container in C#?
Handcrafted Dependency Injection
A simple approach would be to pass a function of the dependencies it needs as parameters or to register the components in a central location from where they can then be retrieved.
That corresponds then approximately to the beginning of a service locator, which is considered in object-oriented programming frequently as an unappreciated way of coding.
Finally, the much simpler and more pragmatic solution represents the frequently overdeveloped DI containers.
Once again, the KISS principle applies here.
If You Are Eager, Pick a Framework
The Angular framework tries to emulate a similar construct here, but this entails a surprisingly (or frighteningly) high degree of complexity and effort.
Furthermore, it uses TypeScript (the superset of JavaScript).
They built the entire DI into Angular’s module system. When you start your app, your root module wires up the dependencies you require throughout the app and allows you to defer some of this dependency resolution to feature modules.
For testing them, you do the same thing with a TestBed (this is just a module at a smaller scale), providing only the dependencies you need to run the tests.
If Angular isn’t your thing or you just can’t use it at all, there is another framework called InversifyJS. It provides a different way of configuring your dependencies, but the same level of control.
Using a framework for DI is a good thing since you can reduce the amount of boilerplate you’ll need to write.
A framework additionally supports you to maintain your code with less effort.
Conclusion
Dependency injection solves a problem in statically typed, object-oriented languages that would not exist if they were no static type system.
The concept is not a value being implemented at any cost.
As is so often the case, it is better to first become familiar with the JavaScript language and then uses it as it suits you — instead of adopting the patterns known from C# or other languages more or less one-to-one in the expectation that both worlds would work in the same way.
Lessons and examples are booooring? Neither remember what you’ve coded in your last video course nor what you’ve learned? Be together with your favorite heroes and characters of your favorite Games, Movies, and TV Shows in my ArnoldCode Academy.
Upvote
Arnold Abraham
Adventures instead of dull tutorials in Full Stack Web and C# Development. Diploma Engineer, Freelancer & Founder of ArnoldCode Academy. Writing on Medium (arnoldcode.medium.com) and on Substack (https://arnoldcodefae.substack.com/?r=e07d1&utm_campaign=pub&utm_medium=web&utm_source=copy).

Related Articles