前言
相较于类装饰器,成员装饰器更加常用一些。
关键词
成员装饰器,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 {
@d
prop1: string;
@d
prop2: number;
@d
static prop3: string;
}
function d(target: any, key: string) {
if (!target.__props) target.__props = [];
target.__props.push(key);
}
class A {
@d
prop1: string;
@d
prop2: number;
@d
static 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 {
@enumerable
method1() {}
static method2() {}
@enumerable
method3() {}
}
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 {
@enumerable
method1() {
console.log('method1 执行了');
}
static method2() {}
@enumerable
@useless
method3() {
console.log('method3 执行了');
}
}
const a = new A();
for (const key in a) {
console.log(key); // => method1 method3
(a as any)[key]();
}
如果一个方法不再使用了,只要添加一个 useless 装饰器即可。当我们和同事共同开发的时候,这种做法也能够帮我们降低不小的开发、沟通成本。