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

是否存在支持对象懒记忆化、首次访问时才加载的npm包?

是否存在可实现对象懒记忆化(memoize)、首次访问对象时才触发加载的npm包?

问题场景

问题复现代码如下:

// service
type EntityType = 'user' | 'post' | 'page'

class Service {
  private readonly pathMap = {
    user: process.env.USER_PATH,
    post: process.env.POST_PATH,
    page: process.env.PAGE_PATH,
  }
  
  getPath(entityType: EntityType) {
    return this.pathMap[entityType];
  }
}

export const service = new Service();

// service.spec.ts
import { service } from './service';
import { loadEnvVars } from '@app/loadEnvVars';

describe('service', () => { 
  beforeAll(loadEnvVars);

  it('should return path', () => {
    expect(service.getPath('user')).toBe(process.env.USER_PATH);
    expect(service.getPath('post')).toBe(process.env.POST_PATH);
    expect(service.getPath('page')).toBe(process.env.PAGE_PATH);
  });
});

上述测试用例会执行失败:导入service模块时单例service会在loadEnvVars执行前完成加载,早于beforeAll钩子的执行时机,导致service的pathMap中读取到的环境变量均为undefined。

现成可用的npm包

目前已经有成熟的npm包实现了你需要的懒记忆化对象能力,不需要重复发布工具包:

  • lazy-value:核心实现和你写的Proxy版本逻辑基本一致,专门用于实现值的懒初始化,首次访问属性时才会执行传入的工厂函数生成目标对象并缓存结果,默认返回只读代理,也支持配置开启写入能力,完全匹配你当前的使用场景。
  • p-lazy:适合工厂函数异步返回Promise的场景,同步场景下能力和lazy-value对齐。
  • memoizee:通用型记忆化工具,支持配置懒初始化模式,除了基础的懒加载能力外,还支持缓存过期、缓存数量限制等进阶配置,如果后续有复杂缓存需求可以选用。

自实现版本的可优化点

你自己写的lazy工具函数目前能覆盖当前测试场景,但还有几个边缘case没有处理,直接用于生产环境可能出现不符合预期的问题:

  • 没有拦截in操作符、Object.keys()、Object.getOwnPropertyNames()这类属性判断、对象遍历逻辑,初始化前调用这些方法会直接返回空代理对象的结果,不会触发工厂函数执行。
  • 没有处理属性写入逻辑,如果在初始化前给代理对象赋值,值会被写到空的代理目标上,不会同步到后续初始化的真实对象中。
  • 没有处理工厂函数执行报错的场景,如果第一次执行工厂函数抛出异常,后续每次访问都会重复执行工厂函数,没有缓存错误状态。

针对当前场景的其他可选修复方案

针对你这个测试加载顺序的问题,除了懒加载对象之外,还有两个更轻量的修复方式,不需要引入额外依赖:

  • 不在模块顶层直接实例化service,改为导出Service类,在测试的beforeAll钩子中执行完loadEnvVars后再初始化单例。
  • 把pathMap的初始化逻辑从类构造阶段挪到getPath方法内部,用类的私有变量做缓存判断,不需要依赖Proxy就能实现懒加载。

注意:你当前实现的lazy返回值默认是只读对象,也可以根据实际需求调整为支持写入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 10:09:47