共计 4523 个字符,预计需要花费 12 分钟才能阅读完成。
TypeScript 中装璜器 decorator 的常识
本文次要介绍对于 TS 中装璜器 Decorator 的相干内容以及应用场景;介绍如何实现一个简略 DI 依赖注入例子;
为什么须要装璜器?
新技术的呈现都是在解决问题,TypeScript 中的装璜器理论是实现了 ECMAScript 对于装璜器的提案。目标是解决以下问题:
- 元数据注入(reflect-metadata):能够为类、办法或属性上增加元数据,这些元数据在运行时被动静拜访和应用【想想 DI,依赖注入的场景,大部分都是靠装璜器来进行依赖关系的传递】。
- 性能扩大 :能够在不批改原始类的定义状况下,对性能进行扩大和批改。比方:增加埋点、解决日志记录、权限校验等。这个性能和装璜器设计模式性能统一。设计模式的应用能让咱们的代码设计更加优雅(更容易了解,健壮性也更好)。
- 代码重用 :一个装璜器函数能够在多个类、办法、属性上进行重复使用。装璜器设计模式很难这点(要做到就很难,抽象层次很高,把装璜器类变成公交车,谁都能用才行)。
装璜器的应用以及分类
咱们把装璜器函数叫做装璜器 (实际上就是一个函数),把利用 @decoratorFunc 的形式叫做装璜器利用。来个简略的 demo:
// 应用 webDecorator 润饰器来润饰 User 类 | |
@webDecorator | |
export class User { | |
private name: string; | |
constructor(name: string) {this.name = name;} | |
} | |
// 定义一个类润饰器,只须要定义一个参数 | |
function webDecorator<T extends {new (...args: any[]): {}}>(TargetConstructor:T) { | |
return class extends TargetConstructor {private registerOrigin = "WEB-SITE";}; | |
} | |
// 定义一个类润饰器 | |
function appDecorator<T extends {new (...args: any[]): {}}>(TargetConstructor:T) { | |
return class extends TargetConstructor {private registerOrigin = "APP";}; | |
} |
import {User} from './user.ts'; | |
function testUserRegister() {const user = new User('Tony'); | |
console.log(user); | |
// {"name":"Tony","registerOrigin":"WEB-SITE"} | |
} | |
testUserRegister(); |
输入的后果:
装璜器的类型依照利用的指标类型进行分类,不同类别的装璜器函数签名不同。分类如下:
-
类装璜器(Class Decorators), 利用与类申明之前的装璜器,能够用来润饰类的行为(比方附加一些函数办法)、增加元数据或动态成员。
- 参数:类装璜器接管一个参数,也就是被装璜类的构造函数(看上边的一个列子 demo)。
- 示例:
function classDecorator(target: { new(): {}}) {...}
- demo 演示(参见上一个例子)
-
办法装璜器(Method Decorators), 用于类办法申明之前的装璜器 ,能够用来批改办法的行为、能够进行参数校验甚至补充逻辑,是执行一些与业务逻辑无关的副作用的最佳抉择(比方:发送埋点、日志记录、权限查看等等)。
- 参数:承受三个参数,别离是被装璜的类的原型对象、办法名称和属性描述符。
- 示例:
function methodDecorator(target: Object, propertyKey: string | symbol, descriptor: PropertyDescriptor) {...}
- demo 演示
@webDecorator | |
export class User { | |
private name: string; | |
constructor(name: string) {this.name = name;} | |
@methodDecorator | |
greet(str: string) {return this.name + ":" + str;} | |
say() {} | |
} | |
// 定义一个 methodDecorator | |
function methodDecorator( | |
target: object, | |
propertyKey: string | symbol, | |
descriptor: PropertyDescriptor | |
) { | |
const value = descriptor.value; | |
descriptor.value = function (...args: any[]) {const result = value.apply(this, args); | |
//... | |
return result + "##"; | |
}; | |
return descriptor; | |
} | |
// 定义一个类润饰器 | |
function webDecorator<T extends {new (...args: any[]): {}}>(TargetConstructor: T) { | |
return class extends TargetConstructor {private registerOrigin = "WEB-SITE";}; | |
} |
-
属性润饰器(Property Decorators),利用于类的属性申明之前的装璜器 ,个别用来批改属性的行为或者增加元数据。
- 参数:接管两个参数,别离是被润饰的类的原型对象和属性名称。
- 示例:
function propertyDecorator(target: Object, propertyKey: string | symbol){...}
- demo
// 属性装璜器函数 | |
function logProperty(target: any, propertyKey: string) {let value = target[propertyKey]; | |
// 属性 getter | |
const getter = function () {console.log(`Getting value of property ${propertyKey}: ${value}`); | |
return value; | |
}; | |
// 属性 setter | |
const setter = function (newValue: any) {console.log(`Setting value of property ${propertyKey} to: ${newValue}`); | |
value = newValue; | |
}; | |
// 从新定义属性 | |
Object.defineProperty(target, propertyKey, { | |
get: getter, | |
set: setter, | |
enumerable: true, | |
configurable: true, | |
}); | |
} | |
// 示例类 | |
class MyClass { | |
@logProperty | |
myProperty: string = 'Initial value'; | |
} | |
// 应用示例 | |
const instance = new MyClass(); | |
console.log(instance.myProperty); // 获取属性值 | |
instance.myProperty = 'New value'; // 设置属性值 | |
console.log(instance.myProperty); // 再次获取属性值 |
- 参数装璜器(Parameter Decorators 利用于类参数或函数参数申明之前的装璜器 。这个就不说了,和属性润饰器相似。
DI 依赖注入
借助 TypeScript 提供的 装璜器能力以及 reflect-metadata 解决元数据的能力,InversfyJS 提供了 DI(dependency injection)实现。DI 的应用能大幅晋升代码的松耦合体现,依赖之间基于接口和形象而不是具体实现。
tips:代码调试倡议在 codesandbox 上,库依赖好解决不必本人到处装置。
npm install inversify reflect-metadata
// interface.ts | |
export interface ILogger {log(msg: string): void; | |
} | |
export interface IService {doing(): void; | |
} | |
export const LoggerID = Symbol("Logger"); | |
export const ServiceId = Symbol("Service"); |
// service.ts | |
import {injectable, inject} from "inversify"; | |
import {ILogger, IService, LoggerID} from "./interface"; | |
import "reflect-metadata"; | |
// 定义一个解决日志的类实现 ILogger 接口 | |
@injectable() | |
export class Logger implements ILogger {log(msg: string) {console.log("logger:", msg); | |
} | |
} | |
// 定义一个生产 Logger 的 Example 类,这里 logger 并不必咱们本人初始化 new,而是应用 inject 装璜器来获取。@injectable() | |
export class Example implements IService {@inject(LoggerID) | |
private logger: ILogger; | |
// 代码依赖于接口,而不依赖具体实现了。爽歪歪 | |
doing() {this.logger.log("your DI is runing!!"); | |
} | |
} |
// container.ts | |
import {Container} from "inversify"; | |
import {ILogger, IService, LoggerID, ServiceId} from "./interface"; | |
import {Logger, Example} from "./service"; | |
import "reflect-metadata"; | |
// 初始化容器 | |
export const DI = new Container({autoBindInjectable: true}); | |
// 向容器中绑定映射对象,容器会默认进行初始化,不必操心。DI.bind<ILogger>(LoggerID).to(Logger); | |
DI.bind<IService>(ServiceId).to(Example); |
// app.tsx | |
import "./styles.css"; | |
import {IService, ServiceId} from "./interface.ts"; | |
import {DI} from './container.ts'; | |
export default function App() {const handleClick = () => {const service = DI.get<IService>(ServiceId); | |
service.doing();}; | |
return ( | |
<div className="App"> | |
<button onClick={handleClick}>DI</button> | |
</div> | |
); | |
} |
下篇咱们聊聊 metadata 元数据。
正文完
发表至: typescript
2023-07-12