前言
相较于类装饰器,成员装饰器更加常用一些。
关键词
成员装饰器,PropertyDescriptor
参考资料
- Object.defineProperty:链接
notes
属性
属性装饰器也是一个函数,该函数需要两个参数:
参数1:如果是静态属性,则为类本身;如果是实例属性,则为类的原型
参数2:固定为一个字符串,表示属性名
方法
方法装饰器也是一个函数,该函数需要三个参数:
参数1:如果是静态方法,则为类本身;如果是实例方法,则为类的原型;
参数2:固定为一个字符串,表示方法名;
参数3:属性描述对象(PropertyDescriptor);
使用 PropertyDescriptor 来约束属性描述符对象。
codes
function d(target: any, key: string) {console.log(key, target, target === A.prototype, target === A);}class A {@dprop1: string;@dprop2: number;@dstatic prop3: string;}

function d(target: any, key: string) {if (!target.__props) target.__props = [];target.__props.push(key);}class A {@dprop1: string;@dprop2: number;@dstatic prop3: string;}console.log((A.prototype as any).__props); // [ 'prop1', 'prop2' ]
class A {}const a = new A();Object.defineProperty(a, "abc", {writable: false,value: "123",enumerable: false,})/*属性描述符对象:{writable: false,value: "123",enumerable: false,}*/
这个属性描述符对象描述的是 a.abc 的相关信息:
- writable:是否能够被赋值
- value:属性值
- enumerable:是否可被枚举
export {};function d() {return function (target: any, key: string, descriptor: PropertyDescriptor) {console.log(key, target === A.prototype, target === A, descriptor);};}class A {@d()method1() {}@d()static method2() {}}const a = new A();for (const key in a) {console.log(key);}

从打印的属性描述符对象可以看出,方法是不可以被枚举的,我们可以改写属性描述符对象身上的 enumerable 属性,令实例方法可以被枚举。
export {};function d() {return function (target: any, key: string, descriptor: PropertyDescriptor) {descriptor.enumerable = true;};}class A {@d()method1() {}@d()static method2() {}@d()method3() {}}const a = new A();for (const key in a) {console.log(key); // => method1 method3}
枚举实例身上的所有属性。
注意,被 static 修饰符修饰的方法 method2 不是实例方法,所以无法被枚举。
通过 a.method2 访问是错误的
通过 A.method2 访问是正确的
export {};function enumerable(target: any, key: string, descriptor: PropertyDescriptor) {descriptor.enumerable = true;};class A {@enumerablemethod1() {}static method2() {}@enumerablemethod3() {}}const a = new A();for (const key in a) {console.log(key); // => method1 method3}
将 @enumerable 单独提取出来
将其单独提取出来,想让哪个实例方法能够被遍历,就往哪个方法上边加 @enumerable 即可,使用起来会更加灵活。
export {};function enumerable(target: any, key: string, descriptor: PropertyDescriptor) {descriptor.enumerable = true;}function useless(target: any, key: string, descriptor: PropertyDescriptor) {descriptor.value = function () {console.log(`${key}方法已过期`);};}class A {@enumerablemethod1() {console.log('method1 执行了');}static method2() {}@enumerable@uselessmethod3() {console.log('method3 执行了');}}const a = new A();for (const key in a) {console.log(key); // => method1 method3(a as any)[key]();}

如果一个方法不再使用了,只要添加一个 useless 装饰器即可。当我们和同事共同开发的时候,这种做法也能够帮我们降低不小的开发、沟通成本。
