前端学 IOC(4)- 另外三种收集依赖的方法

56 阅读2分钟

第二篇文章讲过,我们可以通过 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框架为了收集依赖,确实是各显神通..