You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
typeConsturctor={new(...args: any[]): any};functionSchool<TextendsConsturctor>(BaseClass: T){returnclassextendsBaseClass{// 新增属性 schoolpublicschool='qinghua'};}
@SchoolclassStudent{getSchool(){returnthis.school;// Property 'school' does not exist on type 'Student'}}newStudent().school// Property 'school' does not exist on type 'Student'
// class Aimport{Provider}from"../provide";import{Inject}from"../inject";importBfrom'./classB'importCfrom'./classC'
@Provider('a')exportdefaultclassA{
@Inject()privateb: B
@Inject()c: Cprint(){this.c.print()}}// class Bimport{Provider}from'../provide'
@Provider('b',[10])exportdefaultclassB{n: numberconstructor(n: number){this.n=n}}// class Cimport{Provider}from'../provide'
@Provider()exportdefaultclassC{print(){console.log('hello')}}
2.6、初始化
我们能从以下示例结果中看到,我们已经实现了一个基本的 IOC 容器能力。
import{Container}from'./container'import{load}from'./load'import{class_path}from'./constant'constinit=function(){constcontainer=newContainer()// 通过加载,会先执行装饰器(设置元数据),// 再由 container 统一管理元数据中,供后续使用load(container,class_path)consta:any=container.get('a')// A { b: B { n: 10 }, c: C {} }console.log(a);a.c.print()// hello}init()
总结
本文的依次从 TS 装饰器、Reflect Metadata、IOC 容器源码简单解读、以及自定义实现 IOC 容器四个部分由零到一编写自定义 IOC 容器,希望对你有所启发。本文的所有演示实例都已经上传到 github 仓库 ioc-container ,读者可以克隆下来进行调试运行。
前言
本文的编写主要是最近在使用 midway 编写后端应用,midway 的 IOC 控制反转能力跟我们平时常写的前端应用,例如 react、vue 这些单应用还是有蛮大区别的,所以促使我想一探究竟,这种类 Spring IOC 容器是如何用 JavaScript 来实现的。为方便读者阅读,本文的组织结构依次为 TS 装饰器、Reflect Metadata、IOC 容器源码简单解读、以及自定义实现 IOC 容器。阅读完本文后,我希望你能有这样的感悟:元数据(metadata)和 装饰器(Decorator) 本是 ES 中两个独立的部分,但是结合它们, 竟然能实现 控制反转 这样的能力。本文的所有演示实例都已经上传到 github 仓库 ioc-container ,读者可以克隆下来进行调试运行。
辛苦整理良久,还望手动点赞鼓励~
博客 github地址为:github.com/fengshi123/blog ,汇总了作者的所有博客,欢迎关注及 star ~
一、TS 装饰器
1、类装饰器
(1)类型声明
参数:
target: 类的构造器。
返回:
如果类装饰器返回了一个值,它将会被用来代替原有的类构造器的声明。因此,类装饰器适合用于继承一个现有类并添加一些属性和方法。例如我们可以添加一个 toString 方法给所有的类来覆盖它原有的 toString 方法,以及增加一个新的属性 school,如下所示
但是存在一个问题:装饰器并没有类型保护,这意味着在类装饰器的构造函数中新增的属性,通过原有的类实例将报无法找到的错误,如下所示
这是 一个TypeScript的已知的缺陷。 目前我们能做的可以额外提供一个类用于提供类型信息,如下所示
2、属性装饰器
(1)类型声明
返回的结果将被忽略。
我们可以通过属性装饰器给属性添加对应的验证判断,如下所示
3、方法装饰器
(1)类型声明:
方法装饰器不同于属性装饰器的地方在于 descriptor 参数。 通过这个参数我们可以修改方法原本的实现,添加一些共用逻辑。 例如我们可以给一些方法添加打印输入与输出的能力
4、访问器装饰器
访问器装饰器总体上讲和方法装饰器很接近,唯一的区别在于描述器中有的 key 不同:
方法装饰器的描述器的 key 为:
访问器装饰器的描述器的key为:
例如,我们可以对访问器进行统一更改:
5、参数装饰器
类型声明:
返回的值将会被忽略。
单独的参数装饰器能做的事情很有限,它一般都被用于记录可被其它装饰器使用的信息。
6、执行时机
装饰器只在解释执行时应用一次,如下所示,这里的代码会在终端中打印 apply decorator,即便我们其实并没有使用类 A。
7、执行顺序
不同类型的装饰器的执行顺序是明确定义的:
示例如下所示
我们从上注意到执行实例属性 prop 晚于实例方法 method 然而执行静态属性 static prop 早于静态方法static method。 这是因为对于属性/方法/访问器装饰器而言,执行顺序取决于声明它们的顺序。
然而,同一方法中不同参数的装饰器的执行顺序是相反的, 最后一个参数的装饰器会最先被执行。
8、多个装饰器组合
我们可以对同一目标应用多个装饰器。它们的组合顺序为:
如下示例所示
二、Reflect Metadata
1、背景
在 ES6 的规范当中,ES6 支持元编程,核心是因为提供了对 Proxy 和 Reflect 对象的支持。简单来说这个 API 的作用就是可以实现对变量操作的函数化,也就是反射。然而 ES6 的 Reflect 规范里面还缺失一个规范,那就是 Reflect Metadata。这会造成什么样的情境呢?
由于 JS/TS 现有的 装饰器更多的是存在于对函数或者属性进行一些操作,比如修改他们的值,代理变量,自动绑定 this 等等功能。但是却无法实现通过反射来获取究竟有哪些装饰器添加到这个类/方法上... 这就限制了 JS 中元编程的能力。
此时 Relfect Metadata 就派上用场了,可以通过装饰器来给类添加一些自定义的信息。然后通过反射将这些信息提取出来(当然你也可以通过反射来添加这些信息)。
综合一下, JS 中对 Reflect Metadata 的诉求,简单概括就是:
2、使用
TypeScript 在 1.5+ 的版本已经支持 reflect-metadata,但是我们在使用的时候还需要额外进行安装,如下所示
关于 reflect-metadata 的基本使用 api 可以阅读 reflect-metadata 文档,其包含常见的增删改查基本功能,我们来看下其基本的使用示例,其中 Reflect.metadata 当作 Decorator 使用,当修饰类时,在类上添加元数据,当修饰类属性时,在类原型的属性上添加元数据,如:
当然跟我们平时看到的 IOC 不同,我们进一步结合装饰器,如下所示,与前面的功能是一样的
3、design:类型元数据
在 TS 中的 reflect-metadata 的功能是经过增强的,其添加 "design:type"、"design:paramtypes" 和 "design:returntype" 这 3 个类型相关的元数据
示例如下所示
三、IOC 容器实现
1、源码解读
我们可以从 github 上克隆 midway 仓库的代码到本地,然后进行代码阅读以及 debug 调试。本篇博文我们主要想探究下 midway 的依赖注入合控制反转是如何实现的,其主要源码存在于两个目录:packages/core 和 packages/decorator,其中 packages/core 包含依赖注入的核心实现,加载对象的class,同步、异步创建对象实例化,对象的属性绑定等。
IOC 容器就像是一个对象池,管理着每个对象实例的信息(Class Definition),所以用户无需关心什么时候创建,当用户希望拿到对象的实例 (Object Instance) 时,可以直接拿到依赖对象的实例,容器会 自动将所有依赖的对象都自动实例化。packages/core 中主要有以下几种,分别处理不同的逻辑:
packages/decorator 包含装饰器 provide.ts、inject.ts 的实现,在midwayjs中是有一个装饰器管理类DecoratorManager, 用来管理 midwayjs 的所有装饰器:
2、简单实现
2.1、装饰器 Provider
实现装饰器 Provider 类,作用为将对应类注册到 IOC 容器中。
2.2、装饰器 Inject
实现装饰器 Inject 类,作用为将对应的类注入到对应的地方。
2.3、管理容器 Container
管理容器 Container 的实现,用于绑定实例信息并且在对应的地方获取它们。
2.4、加载类文件 load
启动时扫描所有文件,获取文件导出的所有类,然后根据元数据进行绑定。
2.5、示例类
三个示例类如下所示
2.6、初始化
我们能从以下示例结果中看到,我们已经实现了一个基本的 IOC 容器能力。
总结
本文的依次从 TS 装饰器、Reflect Metadata、IOC 容器源码简单解读、以及自定义实现 IOC 容器四个部分由零到一编写自定义 IOC 容器,希望对你有所启发。本文的所有演示实例都已经上传到 github 仓库 ioc-container ,读者可以克隆下来进行调试运行。
辛苦整理良久,还望手动点赞鼓励~
博客 github地址为:github.com/fengshi123/blog ,汇总了作者的所有博客,欢迎关注及 star ~
The text was updated successfully, but these errors were encountered: