JavaScript 的 getter/setter 可在不改变调用方式下拦截属性读写,实现校验、转换等逻辑;配合 Proxy 可批量动态代理,但需规避无限循环、异步陷阱及 JSON 序列化限制。

通过 JavaScript 的 getter 和 setter,你可以在不改变外部调用方式的前提下,拦截对对象属性的读取与赋值操作,实现透明代理、数据校验、格式转换、依赖追踪等能力。关键在于把逻辑“藏”在属性访问背后,让使用者感觉只是在操作普通属性。
用 getter/setter 封装属性访问逻辑
直接在对象字面量或构造函数中定义 get 和 set,可将真实数据存在私有字段(如用下划线前缀或 Symbol),对外暴露“干净”的属性名:
- 避免暴露内部存储细节,提升封装性
- 读取时(
get)可做默认值处理、计算返回、日志记录 - 写入时(
set)可做类型检查、范围限制、格式标准化(如转小写、截断字符串)
例如:
const user = {_email: '',
get email() { return this._email; },
set email(value) {
if (typeof value === 'string' && value.includes('@')) {
this._email = value.trim();
} else {
throw new Error('邮箱格式不合法');
}
}
};
配合 Proxy 实现批量、动态代理
手动为每个属性写 getter/setter 不可扩展。用 Proxy 可统一拦截整个对象的访问行为,再结合内部校验规则表,实现更灵活的透明控制:
-
get(target, prop)拦截读取:可返回计算值、触发副作用、代理到子对象 -
set(target, prop, value)拦截赋值:先校验再存,失败则抛错或静默处理 - 校验规则可集中配置(如
{ age: { type: 'number', min: 0, max: 150 } }),便于复用和维护
这样,新增属性无需改代码,只需更新规则表,代理逻辑自动生效。
注意陷阱与边界情况
getter/setter 看似简单,但实际使用中容易踩坑:
- 不要在
get中修改自身状态(如递增计数器),否则可能引发无限循环或意外副作用 - 避免在
set中直接触发异步操作(如发请求),因为调用方无法 await;应设计为显式方法(如save()) - JSON 序列化会忽略 getter 返回值(除非自定义
toJSON),需单独处理导出逻辑 - 继承时,子类若重写同名 setter,需显式调用
super.prop = value,否则父类逻辑丢失
适用场景与替代建议
适合轻量级、单对象的数据约束与透明封装,比如表单模型、配置对象、DTO 封装。但若需深度响应式(如 Vue 2)、跨属性联动(如 “总价 = 单价 × 数量”)、或复杂验证链(如 Joi/Yup),更适合用专用库或响应式系统(Proxy + Reflect + 依赖收集)。纯 getter/setter 是基础,不是万能解。

















