第二篇文章讲过,我们可以通过 typescript 的装饰器,配合 Reflect API 的 metadata-extensionECMAScript提案 ,来实现收集依赖,例如:
class A {
b
// 这里通过构造函数上的装饰器 Inject 来声明 A 需要 B 这个依赖
contructor(@Inject('B') b) {
this.b = b
}
}
那么除了这种方式,JS 世界里,还有哪些方式可以收集到依赖呢?
声明式
就是直接声明,某个类所需的依赖,非常直接.. 使用这种方式的有 bottlejs github.com/young-steve…
我们来看下 bottlejs 的 api:
var bottle = new Bottle();
class A {}
class B {}
bottle.service('B', B); // 注册B 并且 B 没有任何依赖
bottle.service('A', A, 'B'); // 注册A ,声明 A 的依赖是 B
// 就是这么直接
这种直接手动声明的方式,优点是不依赖 typscript, 但是显得有些原始
缺点是运行时才会声明依赖,而且首先需要一个 Bottle 实例
假如我只负责编写 class A 呢,我为了声明 A 的依赖,需要在我的文件中先引入 bottle 实例?
Proxy 模式
这种模式也挺秀的,它的本质是,给这个类的构造函数,传入一个Proxy对象,如果你从这个Proxy对象上获取依赖,那么框架就可以拦截到,并提前new一个正确的依赖给你
我们来看个例子:
class B {}
class A {
b
// 这个 opts 其实是框架传入的一个Proxy对象
constructor(opts) {
// 这里通过拦截 opts上的 get 函数,可以得知此处依赖 B 了,框架立即 new 一个 B 并返回
this.b = opts.B
}
}
这个比较简单,我们来写个demo看下
// 下面是随手写的伪代码
class Container {
registered = {};
register(name, _class) {
this.registered[name] = _class;
}
resolve(name) {
const _this = this;
const _class = this.registered[name]
const proxy = new Proxy(
{},
{
get(target, name) {
return _this.resolve(name)
}
}
)
return new _class(proxy)
}
}
const container = new Container()
class B {}
class A {
b
constructor(opts) {
// 这里,会通过 proxy , 感知到需要B 这个依赖,理解 new 一个返回给你
this.b = opts.B
}
}
// 我们来测试下
container.register('A', A) // 注册A
container.register('B', B) // 注册B
const a = container.resolve('A') // 获取A,同时会给A 注入 B
console.log(a.b) // 测试一下:b
toString 模式
这种模式是,通过调用 类或者构造函数的 toString 方法,然后再正则匹配,可以获得这个类的参数...
可以通过这种方式去获取依赖,也是我没有想到的...
举个例子:
class A {
b
contructor(b) {
this.b = b
}
}
A.toString()
// 'class A {\n b\n contructor(b) {\n this.b = b\n }\n}'
// 我们可以通过 toString 这个方法,获取到这个函数,然后,通过正则匹配 contructor 这个函数的参数,可以得知,A 需要 b 这个依赖...
可以看出,各个ioc框架为了收集依赖,确实是各显神通..