Dependency-Injection and JavaScript closures

May 29th, 2010 · 12 Comments ·

As you can tell I am a big fan of DI, but I kept hearing that DI is not needed with dynamic languages, so after using JS for many months I am happy to say that DI is important there as well. In other words dependency managment and controll matters. But it is different.

Say you have a class for greeting with DI.

class Greeter {
  String greeting;
  Greeter(String greeting) {
    this.greeting = greeting;
  }
  void greet(String name) {
    System.out.println(this.greeting + name);
  }
}

Since the class only has one responsibility it only has one method. Now we can rewrite this in JavaScript in OO way like this:

function Greeter(greeting) {
  this.greeting = greeting;
}
Greeting.prototype.greet = function(name){
  alert(this.greeting + name);
};

While this code is fine, it does not follow the spirit of JavaScript which is a functional language. Why have a Greeter noun when you can just have greet verb. (see: Execution in the kingdom of nouns) So let’s rewrite it in functional style:

function greetFactory(greeting) {
  return function(name) {
    alert(greeting + name);
  };
}

so usage changes from:

var greeter = new Greeter('Hello ');
greeter.greet('misko');

to:

var greet = greetFactory('Hello ');
greet('Misko');

Notice that in all cases the DI of greeting is preserved just fine. Now here comes an interesting thing. Objects have scopes. For example the alert method in JavaScript (or System.out.println method in Java) needs to be DI as well since we want to verify that the right thing is printed in test. But the alert method needs to be injected not just into our function but most likely into hundreds of other functions in our application. It is kind of a singleton. So in java we have to do this:

class Greeter {
  String greeting;
  OutputStream out;
  Greeter(OutputStream out, String greeting) {
    this.out = out;
    this.greeting = greeting;
  }

  void greet(String name) {
    out.println(this.greeting + name);
  }
}

The JavaScript equivalent would be:

function greetFactory(alert, greeting) {
  return function(name) {
    alert(greeting + name);
  };
}

But that means that we are no better than Java in injecting the same thing everywhere. The trick is in realizing that object injection is nested in scopes, and that every factory which needs alert share the same alert so we can rewrite this like this;

function createFactories(alert) {
  return {
    greetFactory: function(greeting) {
       return function(greet) {
          alert(greeting + greet);
       };
    },
    someOtherFactory: function(...) {
       return function(...){ alert(...); };
    }
  };
}

When you are ready to bootstarap your app you can:

var factories = createFactories(myAlert);
var greet = factories.createGreet('Hello ');
greet('Misko');

Tags: Uncategorized

12 responses so far ↓

  • Jon // May 29, 2010 at 11:30 pm

    I’m glad to see this post addressing the need for dependency injection in dynamic languages. Declaring a closure inline is the same as newing up a service object in the middle of business logic. It is just as easy to mix solutions into imperative code in a dynamic language as it is in a statically typed language. Regardless of the imperative construct (functions or classes), there needs to be a separation so that declarative solutions may be composed from the imperative building blocks. This is the heart of writing high-quality maintainable code.

  • misko // May 29, 2010 at 11:34 pm

    @jon,

    fully agree, but I find it interesting that closures offer a new way of doing dependency injection which is more concise.

  • dré // May 30, 2010 at 3:56 am

    I’m no JS expert but haven’t you mixed up some variable names (Greeter vs. Greeting, greetFactory vs. createGreet)?
    When you have more than one global service would you do createFactories(global1, global2, …)?
    Functional DI looks very concise! Thanks.

  • Brendan Miller // May 30, 2010 at 10:07 am

    Nice. Yes, closures ftw.

    Being able to use vars in outer scope, and write functions inline (lambdas) simplifies so many problems it is ridiculous…

  • Rafael de F. Ferreira // May 30, 2010 at 3:10 pm

    Interesting post. It looks like the “Dependency Injection As Function Currying” (http://debasishg.blogspot.com/2010/02/dependency-injection-as-function.html) idea implemented in a language lacking syntactic support for currying.

    But there are other ways to think about DI in other “dynamic languages”, for instance check out how Fabio Kung’s post on using ruby mixins to provide dependencies: http://fabiokung.com/2010/05/06/ruby-and-dependency-injection-in-a-dynamic-world/

  • Pedro Newsletter 31.05.2010 « Pragmatic Programmer Issues – pietrowski.info // May 31, 2010 at 1:54 pm

    [...] Misko Hevery article: Dependency-injection and JavaScript closures. [...]

  • bobby // May 31, 2010 at 4:03 pm

    Yea a nice piece of script here i really appericiate this person who post his script and article

  • James Carr // Jun 1, 2010 at 9:27 am

    I would actually opt to inject the function as well:

    (function greetFactory(out, greeting) {
    return function(name) {
    out(greeting + name);
    };
    })(alert, “Hello”)(”World”);

  • James Carr // Jun 1, 2010 at 9:28 am

    Nevermind… after a re-read I realized you did exactly this. Naming the callback “alert” confused me. ;)

  • inou // Jun 5, 2010 at 4:37 pm

    last lines should look like this:
    var factories = createFactories(myAlert);
    var greet = factories.greetFactory(’Hello ‘);
    greet(’Misko’);
    cheers :)

  • Johan // Jun 24, 2010 at 5:10 pm

    You’ve previously said that you think static functions are not OK and death to testability. But isn’t the createFactories function equivalent to a static function? Did you change your mind and accept that static functions are OK as long as you inject dependencies (in this case a pointer to another static-like function)?

    Otherwise I’m a bit puzzled. You said before that the important part is that your tests must be able to prevent the execution of normal code path (or else you would end up with scenario tests instead of unit tests).

  • misko // Jun 24, 2010 at 8:52 pm

    @ Johan,

    I think I got caught red handed. You bring up a good point which I did not think about. You are correct that in this example the factory is a single method without a class which is a static method. But closures kind of undo the staticness of it. In java a static method is static and it has all of the issues I mentioned before, but in JavaScript the method has closure (variables declared outside of the method). This makes the method act as a class to which you pass the dependencies through the constructor (closure) and the class has single method which performs the action (equivalent to just calling the method).

    So in JavaScript a method without a class has injection (closure) but it also has polymorphism, since the method is passed into the consumer, and you can always inject a different method for testing. So closure and methods as first class citizens undo the static method problem of Java.

Leave a Comment