如果没有用过装饰器,很多人应该跟我一样对装饰器具体是个什么东西没什么概念,只知道它是一个样子像 @decorator 的东西,会对被装饰的东西产生影响,但是具体是怎么影响的不清楚。那么,就先用尽量简洁的语言来解释什么是装饰器:
装饰器本质上就是一个包装函数。它可以拿到被装饰的内容,对其进行自定义加工和处理。
一个抽象的解释之后,来看一个简单的例子:
// 装饰器实现
function decorateClass<T>(constructor: T) {
console.log('decorateClass');
}
// 作为类装饰器
@decorateClass
class A {
constructor() {}
}
// 输出结果: decorateClass
装饰器可以拿到类 A,对其进行加工。
装饰器的实现
刚刚已经看了一个简单的装饰器例子,但是装饰器的实现其实不止这种方式,简单总结就是:
-
不支持参数的装饰器
function decorator(target) { // do something with 'target' ... } -
支持参数的装饰器(装饰器工厂) 其实是一个工厂函数,返回一个装饰器方法。
function decorator(value: string) { // this is the decorator factory, it sets up // the returned decorator function return function (target) { // this is the decorator // do something with 'target' and 'value'... }; }
装饰器可以应用在哪些对象上?
知道装饰器的基本概念之后,自然也需要知道到底可以用来装饰什么东西,简单总结就是类相关的它都可以装饰:
- 类(Class Decorators)
- 类属性(Property Decorators)
- 类访问器(Accessor Decorators)
- 类方法(Method Decorators)
- 类方法参数(Parameter Decorators)
类装饰器 - Class Decorators
作用:可以拿到类的构造函数,可以修改或替换类的定义
作用对象:类
参数:类的构造函数
返回值:如果带返回值,则需要是一个构造函数,该构造函数将替换原本类的实现
如下示例,返回一个新的类替换原本类的实现:
function decorateClass(constructor) {
return class B extends constructor {
name = 'B';
age = 18;
}
}
@decorateClass
class A {
name = 'A';
constructor() {
}
}
const a = new A();
console.log(a.name, a.age) // 输出 B 18
function decorateClass(name) {
return function (constructor) {
return class B extends constructor {
name = name || 'B';
age = 18;
};
};
}
@decorateClass('name')
class A {
name = 'A';
constructor() {}
}
const a = new A();
console.log(a.name, a.age); // 输出 name 18
类属性装饰器 - Property Decorators
作用:可以拿到属性名,对属性实现代理等操作
作用对象:静态成员、实例成员
参数:
- target:对于静态成员则是构造函数,对于实例成员则是类的原型
- key:成员名
返回值:无
如下示例,对装饰的属性实现代理,检查设置的值是否合法:
function validProperty(validNameList: string[]) {
return function (target: any, propertyKey: string) {
let value: string = target[propertyKey];
const get = function () {
console.log(`${propertyKey} value: ${value}`);
return value;
};
const set = function (val: string) {
console.log('validNameList', validNameList);
if (!validNameList.includes(val)) {
console.warn(`invalid name: ${val}`);
return;
}
value = val;
};
Object.defineProperty(target, propertyKey, { set, get });
};
}
class People {
@validProperty(['a', 'b'])
name: string;
constructor(name: string) {
this.name = name;
}
}
const people = new People('a');
console.log(people.name); // a
people.name = 'cccc'; // invalid name: cccc
console.log(people.name); // a
类访问器装饰器 - Accessor Decorators
作用:可以修改或替换访问器的定义
作用对象:静态访问器、实例访问器
参数:
- target:对于静态访问器则是构造函数,对于实例访问器则是类的原型
- key:访问器名
- descriptor:属性描述符,即
Object.getOwnPropertyDescriptor(target,key)
返回值:如果带返回值,则需要是一个属性描述符,并作为访问器的属性描述符
如下示例,修改访问器的属性描述符:
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class People {
@validProperty(['a', 'b'])
firstName: string;
lastName: string;
@enumerable(false)
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
constructor(firstName: string, lastName: string) {
this.firstName = firstName;
this.lastName = lastName;
}
}
类方法装饰器 - Method Decorators
作用:可以修改或替换方法的定义
作用对象:静态方法、实例方法
参数:
- target:对于静态方法则是构造函数,对于实例方法则是类的原型
- key:方法名
- descriptor:属性描述符,即
Object.getOwnPropertyDescriptor(target,key)
返回值:如果带返回值,则需要是一个属性描述符,并作为访问器的属性描述符
如下示例,增强方法,补充方法弃用提示:
const deprecated = (deprecationReason: string) => {
return (target: any, memberName: string, propertyDescriptor: PropertyDescriptor) => {
const wrapperFn = (...args: any[]) => {
console.warn(`Method ${memberName} is deprecated with reason: ${deprecationReason}`);
propertyDescriptor.value.apply(target, args)
}
return {
value: wrapperFn,
}
}
}
class TestClass {
static staticMember = true;
instanceMember: string = "hello"
@deprecated("Use another static method")
static deprecatedMethodStatic() {
console.log('inside deprecated static method - staticMember =', this.staticMember);
}
@deprecated("Use another instance method")
deprecatedMethod () {
console.log('inside deprecated instance method - instanceMember =', this.instanceMember);
}
}
TestClass.deprecatedMethodStatic();
const instance = new TestClass();
instance.deprecatedMethod();
类方法参数装饰器 - Parameter Decorators
作用:待补充
作用对象:静态方法、实例方法
参数:
- target:对于静态成员则是构造函数,对于实例成员则是类的原型
- key:参数名
- index:参数在函数参数列表中的索引
返回值:无
如下示例:
function print(target: Object, propertyKey: string, parameterIndex: number) {
console.log(`Decorating param ${parameterIndex} from ${propertyKey}`);
}
class TestClass {
testMethod(param0: any, @print param1: any) {}
}
小结
装饰器的应用
待补充