Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Dependency Injection in JavaScript

DZone's Guide to

Dependency Injection in JavaScript

· Web Dev Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

Dependency injection is about removing the hard coded dependencies and providing a 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 the 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 dependency injection is, 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.

Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:

Published at DZone with permission of Yusuf Aytaş, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}