As an Agile Coach, Miško is responsible for teaching his co-workers to maintain the highest level of automated testing culture, allowing frequent releases of applications with high quality. He is very involved in Open Source community and an author of several open source projects. Recently his interest in Test Driven Developement turned into http://TestabilityExplorer.org with which he hopes will change the testing culture of the open source community. Misko is a DZone MVB and is not an employee of DZone and has posted 38 posts at DZone. You can read more from them at their website. View Full User Profile

Dependency-Injection and JavaScript Closures

05.30.2010
| 6736 views |
  • submit to reddit

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 bootstrap your app you can:

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

From http://misko.hevery.com/2010/05/29/dependency-injection-and-javascript-closures

Published at DZone with permission of Misko Hevery, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Leandro Santos replied on Sun, 2010/05/30 - 3:39pm

 

Misko, great article. I tried to run the code snippets and the last part didn't work for me. Instead of:

 var greet = factories.createGreet('Hello ');

 What I did was:

var greet = factories.greetFactory('Hello ')

 And  it worked for me.

 Thanks,

Leandro.

 

 

 


 

Michael Eric replied on Wed, 2012/09/26 - 3:45pm

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.

debian 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.