可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
With TypeScript now we have static analyze and many OOP features in JavaScript.
So it's also time to have better unit tests in client side logic and as well we need IOC container for dependency injections to make code more testable...
So, have someone already experienced it this subject or maybe know libraries for typescript or JavaScript frameworks that can be porting to TypeScript?
回答1:
I have developed an IoC container called InversifyJS with advanced dependency injection features like contextual bindings.
You need to follow 3 basic steps to use it:
1. Add annotations
The annotation API is based on Angular 2.0:
import { injectable, inject } from "inversify";
@injectable()
class Katana implements IKatana {
public hit() {
return "cut!";
}
}
@injectable()
class Shuriken implements IShuriken {
public throw() {
return "hit!";
}
}
@injectable()
class Ninja implements INinja {
private _katana: IKatana;
private _shuriken: IShuriken;
public constructor(
@inject("IKatana") katana: IKatana,
@inject("IShuriken") shuriken: IShuriken
) {
this._katana = katana;
this._shuriken = shuriken;
}
public fight() { return this._katana.hit(); };
public sneak() { return this._shuriken.throw(); };
}
2. Declare bindings
The binding API is based on Ninject:
import { Kernel } from "inversify";
import { Ninja } from "./entities/ninja";
import { Katana } from "./entities/katana";
import { Shuriken} from "./entities/shuriken";
var kernel = new Kernel();
kernel.bind<INinja>("INinja").to(Ninja);
kernel.bind<IKatana>("IKatana").to(Katana);
kernel.bind<IShuriken>("IShuriken").to(Shuriken);
export default kernel;
3. Resolve dependencies
The resolution API is based on Ninject:
import kernel = from "./inversify.config";
var ninja = kernel.get<INinja>("INinja");
expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true
The latest release (2.0.0) supports many use cases:
- Kernel modules
- Kernel middleware
- Use classes, string literals or Symbols as dependency identifiers
- Injection of constant values
- Injection of class constructors
- Injection of factories
- Auto factory
- Injection of providers (async factory)
- Activation handlers (used to inject proxies)
- Multi injections
- Tagged bindings
- Custom tag decorators
- Named bindings
- Contextual bindings
- Friendly exceptions (e.g. Circular dependencies)
You can learn more about it at https://github.com/inversify/InversifyJS
回答2:
I have created DI library for typescript - huject
https://github.com/asvetliakov/huject
Example:
import {Container, FactoryMethod, ConstructorInject, Inject} from 'huject';
class FirstService {
public constructor(param: number) {}
}
class SecondService {
}
@ConstructorInject
class MyController {
@Inject
public service: FirstService;
public second: SecondService;
public constructor(service: SecondService) {
this.second = service;
}
...
}
container.setAllowUnregisteredResolving(true);
container.register(FirstService, [10]); // Register constructor arguments
// FirstService and SecondService will be resolved for container instance
let controller = container.resolve(MyController);
There is a problem with TypeScript interfaces though, but i have 2 workarounds (use abstract or simple class as interface)
回答3:
Alternatively you can just use no framework and use class as container with object factories as properties. You can then inherit this class in tests and change factories. This approach is type safe and do not require any decorators, just registration of classes.
class B {
echo() {
alert('test');
}
}
class A {
constructor(private b: B) {
b.echo();
}
}
class Container {
A = () => new A(this.B());
B = singleton(() => new B());
}
var c = new Container();
var a = c.A();
// singleton helper:
function memoize<T>(factory: () => T): () => T {
var memo: T = null;
return function () {
if(!memo) {
memo = factory();
}
return memo;
};
}
var singleton = memoize;
回答4:
For now, you can use dependency injection in JavaScript without the IOC part. It is up to you whether you write a "manual" resolver, or factories, or whatever DI pattern you prefer.
When the ECMAScript 6 standard is adopted, it may make the concept of IOC possible in JavaScript.
回答5:
We've been using a simple dependency injection container which uses AMD define/require - like syntax. The original implementation is in TypeScript, although the blog post below presents it in plain old JavaScript.
http://blog.coolmuse.com/2012/11/11/a-simple-javascript-dependency-injection-container/
It's pretty straightforward to define dependency relationships without requiring a bunch of configuration, and supports circular dependency resolution similar to requirejs.
Here's a simple example:
// create the kernel
var kernel = new ServiceKernel();
// define service1
kernel.define("service1", function() {
// service1 has a function named foo
return {
foo: function () { return "foo"; }
}
});
// define service2, which depends on service1
kernel.define("service2", ["service1"], function(service1) {
// service2 has a function named foobar
return {
foobar : function() { return service1.foo() + "bar"; }
}
});
// get service2 instance
var service2 = kernel.require("service2");
service2.foobar(); // returns "foobar"
// get both service1 and service2 instances
kernel.require(["service1", "service2"], function(service1, service2) {
alert(service1.foo() + service2.foobar()); // displays foofoobar
});
回答6:
checkout https://github.com/typestack/typedi
something like this is possible:
import "reflect-metadata";
import {Service, Container} from "typedi";
@Service()
class SomeClass {
someMethod() {
}
}
let someClass = Container.get(SomeClass);
someClass.someMethod();