Skip to content

Dependency Injection in JavaScript

Dependency injection is about removing the hard coded dependencies and providing way of changing dependencies in compile-time or run-time. This pattern has been exercised in several frameworks like Spring(Java). It is also becoming popular in JavaScript community. There are libraries that support dependency injection like Angular.js, Require.js, Inject.js and more. Following recent developments, we will present what is dependency injection, its advantages and simple implementation of this pattern in JavaScript.

Dependency injection is to ask for instance of the classes you need and somebody(injector or factory) provides those instances to you in either compile-time or run-time. There are 3 types of dependency injection : setter, constructor and interface injection. For any of those injection types, one needs a container or injector which can provide dependencies we need. Containers or injectors provides those dependencies by using configuration, types and qualifiers. Now, let’s have a look at the uml diagram below. In this diagram ItemController uses a logger to log its events and it may use one of those Logger implementations. Having configured which one to use, Injector injects instance of Logger to the ItemController.

dependency_injection
Having presented dependency injection, let’s observe why we need this. There can be two main reason to favor dependency injection : first is to improve maintainability of the code, second is to make it easy to test the code. Besides, writing code through an interface is always preferable in terms of abstraction and reusability. By having interfaces instead of implementation, one can change the implementation and remain the rest untouched. This provides you to replace actual implementation with the mock implementation for testing purposes. Furthermore, you can replace implementation of your interface whenever you want by just configuration. Now, let’s see how we implement dependency injection in JavaScript.

var Injector = {
   dependencies: {},
   add : function(qualifier, obj){
      this.dependencies[qualifier] = obj; 
   },
   get : function(func){
      var obj = new func;
      var dependencies = this.resolveDependencies(func);
      func.apply(obj, dependencies);
      return obj;
   },
   resolveDependencies : function(func) {
      var args = this.getArguments(func);
      var dependencies = [];
      for ( var i = 0; i < args.length; i++) {
         dependencies.push(this.dependencies[args[i]]);
      }
      return dependencies;
   },
   getArguments : function(func) {
      //This regex is from require.js
      var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
      var args = func.toString().match(FN_ARGS)[1].split(',');
      return args;
   }
};

The first thing we need a configuration to provide necessary dependencies with qualifiers. To do that, we define a dependency set as dependencies in the Injector class. We use dependency set as our container which will take care of our object instances mapped to qualifiers. In order to add new instance with a qualifier to dependency set, we define an add method. Following that, we define get method to retrieve our instance. In this method, we first find the arguments array and then map those arguments to dependencies. After that, we just construct the object with our dependencies and return it. To figure out, how we use above implementation, we give an example as follows.

var Logger = {
   log : function(log) {}
};
var SimpleLogger = function() {};
var FancyLogger = function(){};

SimpleLogger.prototype = Object.create(Logger);
FancyLogger.prototype = Object.create(Logger);

SimpleLogger.prototype.log = function(log){
   console.log(log);
}

FancyLogger.prototype.log = function(log){
   var now = new Date();
   console.log(now.toString("dd/MM/yyyy HH:mm:ss fff") + " : "+ log);
}

var ItemController = function(logger){
   this.logger = logger;
};
ItemController.prototype.add = function(item) {
   this.logger.log("Item["+item.id+"] is added!");
};
Injector.add("logger", new FancyLogger());
var itemController = Injector.get(ItemController);
itemController.add({id : 5});

Above, we have defined an interface(you can quickly look at how to define interfaces from here) as Logger and implemented Logger with FancyLogger and SimpleLogger. We added instance of FancyLogger to the Injector. After that, we tried to get instance of ItemController via get method of Injector.
In this post, we have tried to give some insight about dependency injection; however, there is a lot left to talk like dependency graph. As this is an introduction for dependency injection concept, we did not go into much detail. You can read source code of angular.js or require.js to find out how there are doing the staff. Consequently, we have tried to explain what is dependency injection and advantages of it as well as implementation of a simple inversion of control container.

Stay updated!

Receive insights on tech, leadership, and growth.

4 Comments

  1. […] “Dependency injection is about removing the hard coded dependencies and providing way of changing dependencies in compile-time or run-time. “とあります(Dependency Injection in JavaScript)。 […]

  2. […] “Dependency injection is about removing the hard coded dependencies and providing way of changing dependencies in compile-time or run-time. “とあります(Dependency Injection in JavaScript)。 […]

  3. Thanks for one’s marvelous posting! I actually enjoyed reading it, you
    will be a great author.I will ensure that I bookmark your blog and will come back later in life.
    I want to encourage yourself to continue your great job, have a nice morning!

  4. Thanks for your good write !

    I would like to share my own JavaScript Dependency Injection Framework:
    https://github.com/di-ninja/di-ninja

    It’s full featured and is currently the only one in javascript, as I know, that implement Composition-Root design pattern,
    helping you to keep all things decoupled and to wire application components and config at one unique root place.
    http://blog.ploeh.dk/2011/07/28/CompositionRoot/

    It work well with NodeJS and Webpack

    Any feedback would be appreciated

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.