乐趣区

关于javascript:面试官问我如何理解-IOC-和-DI

IOC/DI

  • 先附上本人总结的依赖注入 DI 实现源码笔记

谈到依赖注入,必须先了解 IOC 与 DI。

  • IOC,全称 Inversion Of Control,管制反转是面向对象编程的一种设计思维,次要用来升高代码之间的耦合度。
  • DI,全称 Dependency Injection,依赖注入是 IOC 的具体实现。是指对象通过内部的注入,防止对象外部本身实现内部依赖的实例化过程。

IOC 管制反转的设计模式能够大幅度地升高了程序的耦合性。而 装璜器在 VSCode 的管制反转设计模式里,其次要作用是实现 DI 依赖注入的性能和精简局部反复的写法。因为该步骤实现较为简单,咱们先从简略的例子为切入点去理解装璜器的基本原理。

Implementation

@serviceA@serviceB 是参数装璜器,用于解决参数,是由 createDecorator 办法创立的。

  • @参数装璜器应用办法:接管三个参数

    • target: 对于动态成员来说是类的结构器,对于实例成员来说是类的原型链
    • key: 办法的名称,留神是办法的名称,而不是参数的名称
    • index: 参数在办法中所处的地位的下标
  • @返回:返回的值将会被疏忽
class C {constructor(@serviceA private a: A, @serviceB private b: B) {}}

所有参数装璜器均由 createDecorator 办法创立,'A''B',均是该装璜器的惟一标识。

const serviceA = createDecorator("A");
const serviceB = createDecorator("B");

装璜器首先判断是否被缓存,如果有被缓存则取出曾经缓存好的参数装璜器,如果没被缓存,则创立一个 serviceIdentifier 的参数装璜器。

function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {if (_util.serviceIds.has(serviceId)) {return _util.serviceIds.get(serviceId) as ServiceIdentifier<T>;
  }
}

serviceIdentifier 参数装璜器只做了一件事就是触发 storeServiceDependency 把所有依赖项给存起来,存装璜器自身 id,参数的下标 index 以及是否可选 optional

const id = function serviceIdentifier(target: Ctor<T>, key: string, index: number): void {storeServiceDependency(id, target, index, false);
};
id.toString = () => serviceId;
_util.serviceIds.set(serviceId, id);

storeServiceDependency 实质是往 targetclass C 上设置两个动态属性 $di$target$di$dependencies 下面别离存 target,本身还要再存一次本身 target 是为了判断是否曾经存过依赖。

C.$di$target; // class C
C.$di$dependencies[0].id.toString(); // A 或者 B
C.$di$dependencies; // [{id: serviceIdentifier, index: 1, optional: false}, {id: serviceIdentifier, index: 0, optional: false}]

除了存在类上,还存在了 _util.serviceIds 上。

当类申明的时候,装璜器就会被利用,所以在有类被实例化之前依赖关系就曾经确定好了。把 ts 编译就能够证实这点,能够看到 __decorate 在类申明的时候,装璜器就会被执行了,

var C = /** @class */ (function() {function C(a, b) {
    this.a = a;
    this.b = b;
  }
  C = __decorate([__param(0, serviceA), __param(1, serviceB)], C);
  return C;
})();

紧接着就到了 ServiceCollection,这里会将装璜器作为 key 惟一标识,实例化的类作为 value,全副存到 svrsCollection 中,svrsCollection 的实现也很简略,间接用 Map 办法存起来。

const aInstance = new A();
const bInstance = new B();
const svrsCollection = new ServiceCollection();
svrsCollection.set(serviceA, aInstance);
svrsCollection.set(serviceB, bInstance);

后续只须要应用 get 办法并传入对应的参数装璜器就能够获取对应的实例化好的类了。

svrsCollection.get(serviceA); // new A()
svrsCollection.get(serviceB); // new B()

InstantiationService 是实现依赖注入的外围,它是以参数装璜器,例如 serviceAserviceBServiceIdentifierkey 在公有变量 services 中保留所有依赖注入的被实例化好的类。services 保留的是 svrsCollection

const instantiationService = new InstantiationService(svrsCollection);

它裸露了三个公开办法:

  • createInstance 该办法承受一个类以及结构该类的非依赖注入参数,而后创立该类的实例。
  • invokeFunction 该办法承受一个回调函数,该回调函数通过 acessor 参数能够拜访该 InstantiationService 中的所有依赖注入项。
  • createChild 该办法承受一个依赖项汇合,并发明一个新的 InstantiationService 阐明 vscode 的依赖注入机制也是有档次的。

createInstance 办法是实例化的外围办法:

const cInstance = instantiationService.createInstance(C, "L", "R") as C;

首先是获取 ctorOrDescriptor 也就是类 class C 和须要传入非依赖注入的参数 rest

const result = this.createCtorInstance(ctorOrDescriptor, rest);

而后应用 getServiceDependencies 把挂载 class C 动态属性的 $di$dependencies 给获取进去并排序,因为存的时候程序是倒序的

const serviceDependencies = _util
  .getServiceDependencies(ctor)
  .sort((a, b) => a.index - b.index);

取出来的依赖项 serviceDependencies 次要是为了遍历并获取外面的参数装璜器 serviceAserviceB

const serviceArgs: any[] = [];
for (const dependency of serviceDependencies) {const serviceInstance = this.getOrCreateServiceInstance(dependency.id);
  serviceArgs.push(serviceInstance);
}

getOrCreateServiceInstance 实质就是从 servicessvrsCollection 中获取实例化好的类。

const instanceOrDesc = this.services.get(id);
// 相当于 id 即参数装璜器
// svrsCollection.get(id);

当把所有的这些实例化好的类取出来放到 serviceArgs 中后,因为参数装璜器是类实例化的时候就执行完并收集好依赖项,所以 serviceArgs 就是对应 ctorclass C 须要注入的依赖参数,合并非依赖参数就能帮忙咱们胜利实例化好咱们的 ctor 类。

new ctor(...[...serviceArgs, ...args]);

交换

如果文章和笔记能带您一丝帮忙或者启发,请不要悭吝你的赞和珍藏,文章同步继续更新,往期文章也收录在 https://github.com/Wscats/art…
欢迎您的关注和交换,你的必定是我后退的最大能源😁

退出移动版