You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript自定义LazyLoad装饰器Setter未被调用问题排查

问题分析与解决方案

你的问题核心在于装饰器的逻辑方向错了——你试图通过给属性添加setter来触发懒加载逻辑,但你的测试代码从始至终都没有对lazyProperty执行赋值操作,所以这个setter根本就没被调用过,原来的getter一直在正常执行,导致每次访问都会递增counter。

为什么你的setter从未触发?

当你给类的getter装饰时,装饰器操作的是类原型上的属性描述符。你在装饰器里给target(也就是Test的原型)添加了一个setter,但测试代码里只有test.lazyProperty的读取操作,没有任何test.lazyProperty = xxx的赋值行为,这个setter自然不会被触发,原来的getter逻辑完全没被修改,所以每次访问都会重新计算this.counter++。

正确的懒加载装饰器实现

懒加载的核心应该是直接替换原getter,加入缓存逻辑,不需要依赖setter(除非你需要支持修改缓存值)。下面是修正后的代码:

function LazyLoad(target: any, propertyKey: PropertyKey, descriptor: PropertyDescriptor) {
    // 保存原始的getter函数
    const originalGetter = descriptor.get;
    // 用WeakMap缓存每个实例的计算值,避免内存泄漏
    const cache = new WeakMap<any, unknown>();

    console.log('LazyLoad initialized on', target.constructor.name, 'property:', propertyKey);

    // 替换原始getter,加入缓存逻辑
    descriptor.get = function() {
        if (!cache.has(this)) {
            // 第一次访问时,调用原始getter计算值并缓存
            const value = originalGetter?.call(this);
            cache.set(this, value);
            console.log('get (first time, cached)');
        } else {
            console.log('get (returning cached value)');
        }
        return cache.get(this);
    };

    // 如果是只读属性,可以禁用setter,避免意外赋值破坏缓存
    descriptor.set = undefined;
    descriptor.configurable = false;
}

测试代码运行结果

使用你原来的测试代码,运行后会得到符合预期的输出:

LazyLoad initialized on Test property: lazyProperty
get (first time, cached)
0
get (returning cached value)
0
1
2

关键修正点

  1. 直接修改原描述符:装饰器的descriptor参数就是当前属性的描述符,直接修改它的get方法,就能覆盖原始的getter逻辑,确保实例访问属性时走我们的缓存逻辑。
  2. 用WeakMap做实例级缓存:每个类实例有独立的缓存值,并且当实例被垃圾回收时,缓存也会自动清理,不会造成内存泄漏。
  3. 不需要依赖setter:你的场景是只读的懒加载属性,所有操作都是读取,所以完全不需要通过setter来触发缓存初始化。

关于你原代码的额外问题

即使你执行了赋值操作,原代码的setter逻辑也有问题:它会重新定义当前实例的属性为一个新的getter,但同时会把你赋值的newValue覆盖成原始getter的返回值,这显然不是你想要的行为。

内容的提问来源于stack exchange,提问作者Peter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 22:37:44