装饰器是很棒的功能,许多库都是由装饰器组成的,比如 react 和 angular。这是一个很棒的概念
在本文中,我们将学习打字稿装饰器。
什么是 TypeScript 装饰器?
装饰器基本上只是其核心的功能。
使用装饰器,您可以将可重用的行为应用于类、方法和属性。装饰器的灵感来自其他语言(如 Java 和 Python)中的装饰器
装饰器的类型
这些装饰器可以应用于
- Class
- Method
- Class Property
- Accessor and
- Method Parameter
- 类装饰器
类装饰器应用于类的构造函数,可用于观察、修改和更改类定义
类装饰器的例子
type ClassDecorator = <TFunction extends Function>
(target: TFunction) => TFunction | void;
- @Params
- target The constructor of the class
- @Returns
如果装饰器返回一个值。它取代了类定义,是一种用新的属性和方法扩展类定义的方法
让我们借助一个例子来看看这个
function TechClass(constructor: Function) {
console.log(`Class Name: ${constructor.name}`);
}
@TechClass
class CoolClass {
constructor() {
console.log('New Class instance has beed created');
}
}
这里 TechClass 是一个类装饰器,当创建时装饰器被触发并且类名 CoolClass 被记录到控制台
2. 方法装饰器
方法装饰器用于方法,它们可以更改和替换或观察函数定义
让我们通过一个例子来了解更多关于方法装饰器的知识:
function Techfunc(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const realMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`function Name: ${propertyName}`);
return realMethod.apply(this, args);
};
}
class CoolClass {
@Techfunc
testFunc() {
console.log('from testFunc');
}
}
const instance = new CoolClass();
instance.testFunc();
这里techfunc
装饰器将函数名称记录到console
. TestFunc
触发 TechFunc 装饰器的方法,CoolClass
该装饰器又将函数名称记录到控制台
让我们看另一个例子
function CoolLogs(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function (...args) {
console.log('params: ', ...args);
const result = original.call(this, ...args);
console.log('const result: ', result);
return result;
}
}
class C {
@CoolLogs
add(a: number, b:number ) {
return a + b;
}
}
const c = new C();
c.add(1, 2);
// -> params: 1, 2
// -> result: 3
在这里,我们有一个CoolLogs
函数作为名为类的装饰器c
我们的装饰器有 3 个参数
target
propertyKey
descriptor
在const
original 中存储了函数的原始值。
然后原始函数被另一个接受多个参数的函数替换
然后它用相同的参数调用新函数
参数装饰器
参数装饰器用于监视和更改功能或方法参数
让我们看一个例子的参数装饰器
function TestParameter(target: any, propertyName: string, CoolParameter: number) {
console.log(`Cool Parameter: ${CoolParameter}`);
}
class SomeClass {
exampleMethod(@TestParameter saySomething: string) {
console.log(`from exampleMethod: ${saySomething}`);
}
}
const instance = new SomeClass();
instance.exampleMethod('Hello');
这里我们有一个函数TestParameter
,console.logs
参数CoolParameter
然后我们有 SomeClass 和一个名为exampleMethod
. 当exampleMethod
调用时,装饰器被触发并TestParameter
调用该函数,然后将其记录CoolParameter
到控制台
存取装饰器
访问器装饰器类似于方法装饰器,但唯一的区别是方法装饰器在其描述符中具有键,例如
- Value
- writable
- configurable
- enumerable
访问器装饰器中的描述符有键
- get
- set
- enumerable
- configurable
访问器装饰器接受以下参数
- 构造函数或目标原型
- property名称
- 属性描述符 Dead Simple Chat 提供 Javascript Chat API 和 SDK,可在几分钟内将应用内聊天添加到您的 React 应用程序。Dead Simple Chat 是一种高度可定制的聊天解决方案,可用于任何聊天用例。
何时使用装饰器
- Hooks之前/之后
- 观察属性变化和方法调用。
- 转换参数
- 添加额外的方法或属性
- 运行时类型验证
- 自动序列化和反序列化
- 依赖注入
1. 钩子之前/之后
Before / After hooks 表示在调用函数之前和之后调用装饰器,这在调试、记录和测量性能方面非常有用
让我们通过一个例子来研究它
function ExampleOfBeforeAfterHookFunction(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Before: ${propertyName}`);
const result = originalMethod.apply(this, args);
console.log(`After: ${propertyName}`);
return result;
};
}
class SomeClass {
@ExampleOfBeforeAfterHookFunction
coolFunction() {
console.log('Inside coolFunction');
}
}
const instance = new SomeClass();
instance.coolFunction();
这里我们有ExampleOfBeforeAfterHookFunction
函数
originalMethod
我们在哪里存储orignalMethod
const
original
然后我们用一个新的函数实现替换该方法,该函数实现使用...args
属性接受 n 个参数
然后我们记录一个之前的属性名称,然后我们调用该originalMethod
函数,然后我们记录之后的属性名称。
这就是你如何实现 before / after hooks 方法
2. 观察属性变化和方法调用。
Watch 是一个属性装饰器,它记录属性以及装饰方法的方法调用
这在调试和性能监控时特别有用。
让我们通过示例了解有关 Watch 属性及其工作原理的更多信息
function see(target: any, propertyName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`function has been called: ${propertyName} with args:`, args);
return originalMethod.apply(this, args);
};
}
class SomeClass {
myProperty: string;
@see
setProperty(value: string) {
console.log('Set the value to ');
this.myProperty = value;
}
}
const instance = new SomeClass();
instance.setProperty('Awesome Great work Done');
3.变换参数
在转换参数函数中,在调用方法之前将转换函数应用于特定参数
让我们借助一个例子来了解更多关于转换参数的信息
function CoolFunc(SomeFunc: (arg: any) => any): ParameterDecorator {
return (target: any, propertyKey: any, parameterIndex:any) => {
const originalMethod = target[propertyKey];
target[propertyKey] = function (...args: any[]) {
args[parameterIndex] = SomeFunc(args[parameterIndex]);
return originalMethod.apply(this, args);
};
};
}
class MyClass {
myMethod(@CoolFunc((x) => x * 2) num: number) {
console.log(`Transformed parameter: ${num}`);
}
}
const instance = new MyClass();
instance.myMethod(5);
这里发生了什么
方法装饰器查看记录装饰方法的名称和参数
当在 MyClass 的实例中调用方法 see 时,它会记录方法名称,并设置 myproperty 的属性值
4. 添加额外的方法或属性
您可以使用装饰器添加额外的方法或属性。以下是如何使用示例实现此目的
function AddMethod() {
return function (constructor: Function) {
constructor.prototype.AddMethod = function () {
console.log('Added a new method the property by the decorator');
};
};
}
@AddMethod()
class SomeClass {}
const instance = new SomeClass();
(instance as any).AddMethod();
该代码有一个名为的函数AddMethod
,它在装饰类的原型上添加了一个函数,在这种情况下是SomeClass
SomeClass
当调用的实例时,addMethod
可以在其上调用。添加方法将消息记录到控制台
5.运行时类型验证
运行时类型验证确保运行时函数中使用的参数和变量以及常量和数据的值类型与预期类型的值匹配
与软件编译期间发生的静态类型验证不同,运行时类型验证发生在函数或程序运行时,因此它有助于验证外部输入数据
让我们通过示例了解有关运行时类型验证的更多信息
function SomeFunc(target: any, propertyKey: string, parameterIndex: number) {
const originalMethod = target[propertyKey];
target[propertyKey] = function (...args: any[]) {
if (typeof args[parameterIndex] !== 'number') {
throw new Error(`An error has occoured ${parameterIndex}`);
}
return originalMethod.apply(this, args);
};
}
class TestClass {
calculateArea(@SomeFunc radius: number) {
return Math.PI * radius * radius;
}
}
const instance = new TestClass();
try {
instance.calculateArea('this is NaN');
} catch (error) {
console.error(error.message);
}
console.log(instance.calculateArea(6));
6.自动序列化和反序列化
自动序列化和反序列化是指从一种数据类型转换为另一种数据类型反序列化,以便以特定格式存储数据的做法
然后再次从一种数据类型转换为原始数据类型以呈现数据或其他功能可以使用数据
这是一个示例,说明如何使用装饰器来执行此操作
function SerializeFunc(constructor: Function) {
constructor.prototype.serialize = function () {
return JSON.stringify(this);
};
constructor.deserialize = function (json: string) {
return new constructor(JSON.parse(json));
};
}
function DeserializeFunc(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (serializedData: string) {
const deserializedData = JSON.parse(serializedData);
return originalMethod.apply(this, deserializedData);
};
}
@SerializeFunc
class Person {
constructor(public name: string, public age: number) {}
@SerializeFunc
greet(data: { name: string; age: number }) {
return `Awesome, people call me ${data.name} my age is ${data.age} years old.`;
}
}
const man = new Man('Jim Class', 43);
const serializedMan = man.serialize();
console.log(serializedMan); // {"name":"Jim Class","age":43}
const deserializedMan = (Man as any).deserialize(serializedMan);
console.log(deserializedMan.greet());
const serializedData = '{"name":"Don markell","age":56}';
console.log(man.greet(serializedData));
7. 依赖注入
装饰器也可以用于依赖注入。依赖注入是提供方法所依赖的函数和对象作为参数的做法
这有助于解耦代码并使其更清晰
装饰器通过提供一种将依赖项直接注入类构造函数而不更改类实现的方法来帮助依赖注入
这使它们更易于维护和测试
使用装饰器的好处
1. 横切关注点
当程序的某些部分中继程序的许多其他部分时。就像程序可能会依赖其他类的方法或属性一样,这可能是装饰器的一个用例,它们提供了一种维护代码并提高可读性的简单方法
2.依赖注入
依赖注入是一种软件设计模式,其中依赖于其他对象或函数的对象或函数接收然后
这就像控制反转,导致松散耦合的程序和模块化的代码库
装饰器在这里也很有用
3. 验证
装饰器可用于验证输入或对象的状态。
4.代码组织
不属于主函数或逻辑的代码可以使用装饰器封装,从而使代码更具可读性和可维护性
结论
在这篇文章中,我们学习了如何使用 typescript 装饰器,我们还使用了一些示例来使它们更容易理解
原文链接:https://juejin.cn/post/7246010218411622456 作者:happyEnding