今天我们来学习,什么是 ioc 中的 作用域 scope (或者叫 lifetime 这俩概念差不多)
有的库中称之为 scope ,例如 inversify inversify.io/
有的称之为 lifetime ,例如 awilix github.com/jeffijoe/aw…
那么,究竟是什么呢?
最好理解的场景是,单例 Singleton,即,不管什么时候从容器中 get,总是返回同一个对象,不会每次都 new 一个新的给你
例如:
// 按照 inversify 的 api,
// 声明这个 Editor 类是单例的
container.bind('Editor').to(Editor).inSingletonScope();
const editor1 = constainer.get('Editor') // 缓存中没有的话,会 new 一个新的返回
const editor2 = constainer.get('Editor') // 再次get也不会new新的,而是从 缓存中获取
editor1 === editor2 // true
单例的场景没啥好说的
TRANSIENT (瞬态)这个是第二种类型,其实就是,每次 get 都会 new 一个新的给你,一般来说这种是默认的scope
例如
// 还是拿 inversify 的 api 举例,
// 绑定作用域为 瞬态的 TRANSIENT
container.bind('Node').to(Node).inTransientScope();
const node1 = constainer.get('Node') // 每次都 new 一个新的 node 给你
const node2 = constainer.get('Node') //
node1 === node2 // false
下一种类型是 request
这个 scope 比较有说法了,这个意思是,每次 get 的时候,都会按照对应的依赖图,创建一系列对象,而在创建的过程中,如果这个类型的对象已经创建过了,就用缓存的,不再创建新的
同样是缓存,区别于单例的是,单例是全局缓存,只要在这个 container 里创建过,就有缓存
而这个 request 是,本次 get 的过程中,有缓存,而当 get 结束之后,缓存也就清除了
举个例子
// 假设这个场景: A 依赖 B 和 C, 而 B 依赖 C
A => B 而 B => C
=> C
// 如果 C 瞬态的,则每次请求 A,会先创建 B 和 C,
// 而在创建 B 的时候,又会再次创建一次 C,而这两个 C 是不相等的
class A {
b
c
contructor(@Inject('B') b, @Inject('C') c) {
this.b = b
this.c = c
}
}
class B {
c
contructor(@Inject('C') c) {
this.c = c
}
}
class C {
}
// 假设我们将这三个类,都设置为 瞬态的
container.bind('A').to(A).inTransientScope();
container.bind('B').to(B).inTransientScope();
container.bind('C').to(C).inTransientScope();
const a = constainer.get('A') // 此时会遍历 A 的依赖图
// 依赖图类似
A => B => C
=> C
// 根据依赖图 依次构造,因此会创建两次 c
a.c === a.b.c // false
// 而如果我们将 C 的scope 设置为 request 后
container.bind('C').to(C).inRequestScope();
const a = constainer.get('A')
// 此时,在遍历依赖图的时候,第一次创建的 C 会生成缓存,
// 在解析 B 的依赖的时候,会直接拿之前缓存的 c ,不会再创建新的
a.c === a.b.c // true
request 这种还是比较有用的
最后一种是 childScope
意思是, 创建一个小的 “局部范围” ,生成的对象只在这个范围内生效
一个非常主要的场景是,node 里的请求,每个请求都创建一个 “局部范围” ,我们可以在这个 “局部范围” 内放置一些对象(权限,上下文,token,用户等等),这样每个请求之间都互不干扰
这个在 inversify.js 里是不支持的,我们来看下 awilix 的语法
// Increments the counter every time it is resolved.
let counter = 1
// 这个是,将 counterValue 注册为 “工厂类” (有的称之为 factory,有的称之为 function)
container.register({
counterValue: asFunction(() => counter++).scoped(),
})
// 创建两个 scope
const scope1 = container.createScope()
const scope2 = container.createScope()
// scope 还可以再创建子 scope
const scope1Child = scope1.createScope()
scope1.cradle.counterValue === 1
// 第二次访问 counterValue , 因为前一次在 scope1 内有缓存,因而还是1
scope1.cradle.counterValue === 1
// 首次访问 scope2 里的 counterValue , 没有缓存,获得的值为 2
scope2.cradle.counterValue === 2
// 再次访问 ,读取缓存 2
scope2.cradle.counterValue === 2
// 读取 子 scope 的值
scope1Child.cradle.counterValue === 3
OK,就看到这里,下次见