TypeScript 中没有内置的“方法拦截器”,但可以通过几种方式模拟方法拦截的行为,主要是利用高级类型和装饰器(Decorators)功能。方法拦截器通常用于在方法调用前后执行一些逻辑,比如日志记录、性能监控、验证等。
Object.getOwnPropertyDescriptor实现
class DateUtil {
format() {
console.log("format");
}
utc() {}
parse() {}
}
// 1. 通过Object.getOwnPropertyDescriptor获取描述对象
const descriptor = Object.getOwnPropertyDescriptor(
DateUtil.prototype,
"format"
);
// 2. 缓存原始的方法
const method = descriptor?.value;
// 3. 重写原始方法,添加拦截逻辑
if (descriptor) {
descriptor.value = function () {
console.log("before format");
method?.call(this);
console.log("after format");
};
}
// 4. 重新定义类的方法
Object.defineProperty(DateUtil.prototype, "format", descriptor!);
// 5. 测试
const util = new DateUtil();
util.format(); // before format -> format -> after format
// 6. 可以自行封装成一个方法
什么是属性描述对象?
属性描述对象(Property Descriptor)是JavaScript中的一个概念,用于解释对象属性的配置。当你使用Object.getOwnPropertyDescriptor()
方法获取某个对象属性的描述时,或者使用Object.defineProperty()
方法定义新属性或修改现有属性时,都会用到属性描述对象。
属性描述对象可以包含以下字段:
value:属性的值。
writable:布尔值,表示属性的值是否可以被重写。
enumerable:布尔值,表示属性是否可以在
for...in
循环或Object.keys()
方法中被枚举。configurable:布尔值,表示属性的描述是否可以被改变,以及属性是否可以被删除。
get:一个给属性提供getter的方法,如果没有getter则为
undefined
。set:一个给属性提供setter的方法,如果没有setter则为
undefined
。
注意,一个属性描述对象要么是一个数据描述符(拥有value
和writable
),要么是一个存取描述符(拥有get
和set
),两者不能混用。
这个机制提供了一种高级的方式来精确控制对象属性的行为。例如,你可以创建一个只读属性(writable: false
),或者创建一个属性,当它被访问或修改时,可以执行特定的逻辑(通过get
和set
方法)。
Object.defineProperty和proxy的区别?
Object.defineProperty
和Proxy
都是JavaScript中用于控制对象属性行为的机制,但它们在用法和功能上有显著的区别:
Object.defineProperty
用途:允许精确地添加或修改对象的属性。通过属性描述符来控制属性的行为,如可枚举性(enumerable)、可配置性(configurable)、可写性(writable)、以及通过getter和setter定义的访问控制。
局限性:
只能作用于单个属性。
对象中已经存在的属性可以被修改,新属性可以被添加,但需要显式地对每个属性进行操作。
不能直接监听对象属性的添加或删除操作。
不适用于数组索引操作的拦截(如直接通过索引修改数组元素)。
性能:对于单个属性的控制,性能开销较小。
Proxy
用途:
Proxy
是一个更强大和灵活的方式来创建对象的代理,允许你拦截并自定义对象的多种操作,如属性读取、属性设置、枚举、函数调用等。特点:
可以拦截和自定义更多种类的对象操作。
可以直接监听对象的读取、设置属性值、枚举属性、函数调用等操作,而不需要针对每个属性或方法单独设置。
适用于动态修改对象行为的场景,如性能监控、数据绑定、访问控制等。
可以拦截数组操作,如通过索引设置值。
局限性:
由于
Proxy
可以拦截大量操作,如果不恰当使用可能会引入性能问题。一旦对象被代理,无法从外部获取原始对象(除非在代理中特别处理)。
总结
如果你需要对对象的单个属性进行精细控制,特别是当你需要定义属性的getter和setter时,
Object.defineProperty
是一个好选择。如果你需要拦截和自定义对象的多种操作,或者需要对整个对象进行控制,而不仅仅是单个属性,
Proxy
提供了更强大和灵活的机制。