设计思路
- 装饰器的作用:通过装饰器为类添加额外功能,比如日志记录、权限检查等。
- 反射的运用:利用反射机制在运行时获取类的元数据,以便动态操作类的属性和方法。
- Symbol的作用:使用Symbol确保装饰器和反射相关的元数据具有唯一性和安全性,防止命名冲突。
代码结构
- 定义Symbol:
const metadataKey = Symbol('metadata');
- 创建装饰器:
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling method ${propertyKey} with arguments:`, args);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned:`, result);
return result;
};
return descriptor;
}
- 使用装饰器并结合反射:
class MyClass {
@log
myMethod(a: number, b: number) {
return a + b;
}
}
function getMetadata(target: any) {
return Reflect.getMetadata(metadataKey, target);
}
Reflect.defineMetadata(metadataKey, 'Some metadata', MyClass);
console.log(getMetadata(MyClass));
Symbol确保唯一性和安全性
- 唯一性:由于Symbol是唯一的,每次创建新的Symbol即使描述相同,也是不同的值。例如
const s1 = Symbol('test'); const s2 = Symbol('test'); s1!== s2
。这确保了在元编程和反射场景下,不同模块或库使用相同描述创建的Symbol不会冲突,比如不同装饰器使用相同名字的元数据键,使用Symbol可避免此问题。
- 安全性:Symbol不能通过常规属性名访问方式(如
obj.property
)获取,只能通过Reflect
相关操作访问,如Reflect.getMetadata(symbol, target)
。这增加了元数据的安全性,防止意外或恶意的属性访问和修改,保护了类的内部元数据结构。