Getter 和 Setter 是 JavaScript 中拦截属性读写操作的访问器属性,用于自定义获取与设置行为,常见于数据验证、依赖追踪等场景;通过 Object.defineProperty 定义时需注意避免无限递归、属性存在性及 writable 设置。

Getter 和 Setter 是 JavaScript 中用于拦截对象属性读写操作的访问器属性,它们让开发者能自定义属性的获取(get)和设置(set)行为,常用于数据验证、依赖追踪、日志记录或响应式更新等场景。
用 Object.defineProperty 定义 Getter/Setter
这是最基础也是最常用的方式,适用于单个属性的拦截:
- 必须在对象初始化后或属性存在前定义;若属性已存在且为数据属性(即有 value),需先设
writable: false才能改造成访问器 - get 函数不能带参数,返回值即为属性读取结果;set 函数接收一个参数(赋值时传入的值),无返回要求
- 注意避免在 getter 中直接读取自身属性(如
return this.xxx),否则会触发无限递归
示例:
const obj = {};
Object.defineProperty(obj, 'price', {
get() {
console.log('正在读取 price');
return this._price || 0;
},
set(value) {
if (typeof value !== 'number' || value
用 class 语法声明 Getter/Setter
在类中定义更简洁,适合封装实例属性逻辑:
- getter/setter 方法名就是暴露给外部的属性名,无需额外 defineProperty
- 内部通常配合私有字段(如
#price)或下划线命名的“伪私有”属性存储真实值 - 无法对继承来的 getter/setter 进行重写拦截(除非在子类中重新定义)
示例(使用私有字段):
class Product {
#price = 0;
get price() {
console.log('getter 触发');
return this.#price;
}
set price(value) {
if (value
用 Proxy 全局拦截读写(更强大灵活)
当需要批量拦截整个对象的所有属性(包括动态新增的),Proxy 是更现代、更通用的选择:
- 不修改原对象结构,而是创建一个代理对象,所有操作都经由 trap 拦截
-
get(target, prop, receiver)拦截读取;set(target, prop, value, receiver)拦截赋值 - receiver 是触发操作时的 this(用于正确处理原型链上的 getter)
- 可统一处理不存在的属性、数组索引、Symbol 键等,灵活性远超 defineProperty
示例:
const data = { count: 0 };
const proxy = new Proxy(data, {
get(target, prop) {
console.log(`读取 ${prop}:`, target[prop]);
return target[prop];
},
set(target, prop, value) {
console.log(`设置 ${prop} =`, value);
target[prop] = value;
return true; // 必须返回 true 表示赋值成功
}
});
proxy.count++; // 读取 count → 设置 count = 1
注意事项与常见陷阱
实际使用中容易忽略的关键点:
- defineProperty 定义的访问器默认不可枚举(
enumerable: false),for...in 或 Object.keys 看不到该属性;如需遍历,手动设enumerable: true - Proxy 的 set trap 必须显式返回 true,否则赋值静默失败(严格模式下会报错)
- 不要在 getter 中修改对象状态(如 push 到数组、改变其他属性),这会破坏纯函数假设,引发难以调试的副作用
- Vue 2 的响应式系统底层大量使用 defineProperty,但因无法监听新增/删除属性而被 Vue 3 的 Proxy 方案替代










