Object.defineProperties 可通过 get 描述符实现只读自动计算属性,如总价=单价×数量;需设 writable: false、不设 set,支持缓存与依赖追踪,但无法监听深层变化且 JSON 序列化不包含。

用 Object.defineProperties 实现自动计算属性,核心是把“计算逻辑”封装在 get 描述符里,让属性读取时动态执行,而非存储固定值。它不依赖框架,轻量、可控,特别适合业务中需要实时联动的字段(比如总价 = 单价 × 数量、状态文案 = 根据多个字段推导)。
定义只读的自动计算属性
计算属性通常不应被直接赋值,否则会破坏逻辑一致性。通过设置 writable: false 并提供 get,即可实现只读自动计算:
const order = {
price: 89.9,
quantity: 3
};
<p>Object.defineProperties(order, {
total: {
get() {
return Number((this.price * this.quantity).toFixed(2));
},
enumerable: true,
configurable: false
}
});</p><p>console.log(order.total); // 269.70
order.total = 100; // 无效(严格模式下报错)</p>-
get函数内使用this指向当前对象,可安全访问其他属性 -
enumerable: true让该属性出现在for...in和Object.keys()中(按需设置) - 不设
set且writable: false(默认),天然只读
响应式依赖追踪(简易版)
原生 JS 不自动追踪依赖,但可通过手动触发更新,模拟“响应式”。适用于数据变化频率低、或配合事件/方法调用的场景:
const form = {
firstName: '张',
lastName: '三',
_fullName: null
};
<p>Object.defineProperties(form, {
fullName: {
get() {
// 缓存 + 脏检查:仅当依赖变化时重新计算
const current = <code>${this.firstName} ${this.lastName}</code>;
if (this._fullName !== current) {
this._fullName = current;
}
return this._fullName;
},
enumerable: true
}
});</p><p>// 当外部修改 firstName/lastName 后,可显式重置缓存(或结合 Proxy 增强)
form.firstName = '李';
console.log(form.fullName); // '李 三'(下次 get 时自动更新)</p>- 适合简单组合(如姓名、路径拼接),避免每次 get 都重复计算开销
- 若需“自动感知变化”,需配合
Proxy或业务层主动调用更新方法 - 不要在
get中修改this的其他属性,易引发无限循环
封装成可复用的计算属性工厂函数
避免重复写 defineProperties,抽离为工具函数,支持传入计算逻辑和依赖字段名:
function defineComputed(obj, props) {
const descriptors = {};
for (const [key, fn] of Object.entries(props)) {
descriptors[key] = {
get() {
return fn.call(this);
},
enumerable: true,
configurable: true
};
}
Object.defineProperties(obj, descriptors);
}
<p>// 使用
const user = { baseSalary: 12000, bonus: 3000, taxRate: 0.15 };
defineComputed(user, {
grossIncome() {
return this.baseSalary + this.bonus;
},
netIncome() {
return Number((this.grossIncome * (1 - this.taxRate)).toFixed(2));
}
});</p><p>console.log(user.netIncome); // 12750</p>- 函数接收对象和一个映射表:
{ 属性名: 计算函数 } - 每个
get都绑定正确this,支持跨属性引用(如netIncome依赖grossIncome) - 保持简洁,不引入额外依赖,便于嵌入现有代码库
注意事项与边界情况
自动计算属性不是万能的,需注意以下实际限制:
- 无法监听深层嵌套对象的变化(如
user.profile.age改变,user.fullName不会自动更新),需手动处理或升级为Proxy - JSON 序列化时,
get属性默认不被包含(除非toJSON显式返回),导出数据前应转为普通属性 - 调试时看不到计算属性的“值”,只能看到
get函数,在 DevTools 中可能显示为undefined直到首次访问 - 避免在
get中执行副作用(如发请求、改 DOM),它应是纯函数

















