JavaScript可通过get访问器配合Object.defineProperty实现计算属性的延迟加载,即首次访问时计算并重定义为数据属性,后续直接返回缓存值,提升性能且保持接口简洁。

JavaScript 中可以通过属性描述符(Property Descriptor)配合 get 访问器,实现计算属性的延迟加载(Lazy Evaluation),即首次访问时才执行耗时计算,后续直接返回缓存结果。
用 get 描述符定义只读、惰性求值的属性
属性描述符中的 get 函数会在每次读取属性时触发。若在 get 中完成一次计算并用 Object.defineProperty 重新定义该属性为普通数据属性,就能实现“首次计算、后续直取”的效果。
- 避免重复计算,提升性能,尤其适用于开销大、不常变动的派生值(如格式化字符串、深度克隆、DOM 尺寸等)
- 无需额外字段存储缓存状态,自然覆盖原访问器逻辑
- 保持对外接口简洁:使用者仍通过
obj.prop访问,无感知延迟逻辑
基础实现:覆盖自身描述符
核心技巧是:在 get 中计算结果后,调用 Object.defineProperty 将当前属性重定义为具有该值的 writable: true 数据属性,从而移除访问器。
function defineLazyProperty(obj, key, getter) {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
get() {
const value = getter.call(obj);
Object.defineProperty(obj, key, {
value,
writable: true,
enumerable: true,
configurable: true
});
return value;
}
});
}
// 使用示例
const user = { firstName: 'John', lastName: 'Doe' };
defineLazyProperty(user, 'fullName', function() {
console.log('Computing fullName...'); // 仅首次打印
return this.firstName + ' ' + this.lastName;
});
console.log(user.fullName); // Computing fullName... → "John Doe"
console.log(user.fullName); // "John Doe"(不再计算)
支持 setter 和初始值重置的增强版
若需支持手动修改或重置缓存,可保留访问器但内部维护私有缓存字段,并提供 reset 方法或允许写入来清除缓存。
立即学习“Java免费学习笔记(深入)”;
- 用 Symbol 或闭包变量保存是否已计算及结果,避免污染公开属性
- 添加
set时,清空缓存并允许新值覆盖(此时变为“可写懒属性”) - 注意
configurable: false后无法再次 redefine,务必设为true
实际场景建议
延迟加载适合以下情况:
- 对象初始化时未准备好依赖(如 DOM 元素尚未挂载,需首次访问时查询)
- 计算依赖外部不可变状态(如全局配置、URL 参数),且该状态生命周期内不变
- 避免构造函数中阻塞式操作,把开销推迟到真正需要时
不推荐用于频繁变更的值,或计算结果本身可能随外部状态动态变化却无响应机制的场景——此时应考虑 Proxy 或响应式框架。


















