TS 装饰器:
TS 装饰器允许在类上添加额外的功能。在创建类的任何实例之前,装饰器会在声明时间更改类。
语法:
装饰器用@ 符号声明,例如@metadata。 TS 现在将搜索相应的元数据函数,并自动为其提供几个参数,这些参数会根据具体修饰的内容而有所不同(例如,类或类属性获取不同的参数)
这些参数在装饰器函数中提供:
- 类的原型对象
- 属性键或方法名称
- PropertyDescriptor 对象,如下所示
{writable: true, enumerable: false, configurable: true, value: ƒ}
根据装饰器的类型,这些参数中的 1-3 个被传递给装饰器函数。
装饰器类型:
以下装饰器可以应用于一个类,TS 将按以下顺序评估它们(以下总结来自 TS 文档):
- 为每个实例成员应用参数装饰器,然后是方法、访问器或属性装饰器。
- 参数装饰器,后跟方法、访问器或属性
装饰器应用于每个静态成员。
- 为构造函数应用参数装饰器。
- 类装饰器应用于类
更好地理解它们的最佳方法是通过示例。请注意,这些示例确实需要对 TS 语言和 PropertyDescriptor 等概念有深入的了解。
方法装饰器:
function overwrite(
target: myClass,
propertyKey: string,
descriptor: PropertyDescriptor
) {
console.log('I get logged when the class is declared!')
// desciptor.value refers to the actual function fo the class
// we are changing it to another function which straight up
// overrides the other function
descriptor.value = function () {
return 'newValue method overwritten'
}
}
function enhance(
target: myClass,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const oldFunc = descriptor.value;
// desciptor.value refers to the actual function fo the class
// we are changing it to another function which calls the old
// function and does some extra stuff
descriptor.value = function (...args: any[]) {
console.log('log before');
const returnValue = oldFunc.apply(this, args)
console.log('log after');
return returnValue;
}
}
class myClass {
// here is the decorator applied
@overwrite
foo() {
return 'oldValue';
}
// here is the decorator applied
@enhance
bar() {
return 'oldValueBar';
}
}
const instance =new myClass()
console.log(instance.foo())
console.log(instance.bar())
// The following gets logged in this order:
//I get logged when the class is declared!
// newValue method overwritten
// log before
// log after
// oldValueBar
属性装饰器:
function metaData(
target: myClass,
propertyKey: string,
// A Property Descriptor is not provided as an argument to a property decorator due to
// how property decorators are initialized in TypeScript.
) {
console.log('Execute your custom code here')
console.log(propertyKey)
}
class myClass {
@metaData
foo = 5
}
// The following gets logged in this order:
// Execute your custom code here
// foo
类装饰器(来自 TS 文档):
function seal(
constructor: Function,
) {
// Object.seal() does the following:
// Prevents the modification of attributes of
// existing properties, and prevents the addition
// of new properties
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@seal
class myClass {
bar?: any;
foo = 5
}
myClass.prototype.bar = 10;
// The following error will be thrown:
// Uncaught TypeError: Cannot add property bar,
// object is not extensible
装饰器和装饰器工厂:
装饰器可以通过装饰器函数或装饰器工厂函数来声明。语法上的差异最好通过一个例子来解释:
// Returns a decorator function, we can return any function
// based on argument if we want
function decoratorFactory(arg: string) {
return function decorator(
target: myClass,
propertyKey: string,
) {
console.log(`Log arg ${arg} in decorator factory`);
}
}
// Define a decorator function directly
function decorator(
target: myClass,
propertyKey: string,
) {
console.log('Standard argument');
}
class myClass {
// Note the parentheses and optional arguments
// in the decorator factory
@decoratorFactory('myArgument')
foo = 'foo';
// No parentheses or arguments
@decorator
bar = 'bar';
}
// The following gets logged in this order:
// Log arg myArgument in decorator factory
// Standard argument