装饰器现状
- Javascript里的装饰器目前处在 建议征集的第二阶段,
- 但在TypeScript里已做为一项实验性特性予以支持
启用
{
"compilerOptions": {
"target": "ES5",
"experimentalDecorators": true
}
}
简介
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用
@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
实现
- 不支持参数的装饰器
function sealed(target) {
// do something with "target" ...
}
- 装饰器工厂
function color(value: string) { // 这是一个装饰器工厂
return function (target) { // 这是装饰器
// 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) {}
}
复制代码
总结
- 装饰器本质上就是一个包装函数。它可以拿到被装饰的内容,对其进行自定义加工和处理。