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

使用defineProperty扩展getter/setter触发无限循环的原因与解决方案

问题成因

触发无限循环的核心原因有两点:

  1. 你找到的原attribute属性本身是访问器属性(自带get逻辑),你将原属性的访问器描述符直接完整赋值给了obj的Symbol属性。当你执行obj[id]读取这个Symbol属性时,会自动触发原访问器的get逻辑,此时该get逻辑的this指向obj。
  2. 当obj和你重定义属性的目标对象_object是同一个对象时(即_object自身就带有attribute访问器属性的场景),原get逻辑中只要有访问this.attribute的操作,就会触发你新定义的getter,新getter又会再次调用obj[id]执行原get逻辑,形成无限递归,最终导致页面崩溃。

额外注意:你当前用箭头函数定义新的getter,箭头函数没有自身的this,会绑定外层作用域的this,也会导致属性访问的this指向不符合预期。

可行解决方案

不需要额外将原属性拷贝到Symbol名下,直接缓存原访问器的get/set函数,调用时手动绑定当前实例的this即可规避循环问题,修改后的代码如下:

// 查找原型链上的attribute属性描述符逻辑不变
let obj = _object;
while(obj && !Object.getOwnPropertyDescriptor(obj, 'attribute')){
  obj = Object.getPrototypeOf(obj);
}
const attributeDesc = Object.getOwnPropertyDescriptor(obj, 'attribute');
// 缓存原访问器的get/set逻辑
const originalGet = attributeDesc.get;
const originalSet = attributeDesc.set;

// 重定义属性,使用普通函数获取正确的实例this
Object.defineProperty(_object, 'attribute', {
  get: function () {
    // 手动调用原getter,绑定当前实例为this,不走属性访问逻辑避免循环
    const attribute = originalGet.call(this);
    // 执行你的扩展逻辑
    attribute['extend'] = 'property';
    return attribute;
  },
  // 补上原setter逻辑,避免原属性的赋值能力丢失
  set: function (val) {
    originalSet.call(this, val);
  },
  enumerable: false,
  configurable: true
});

如果确实需要通过Symbol属性存储原逻辑,要确保读取Symbol属性时不要触发原访问器的this指向已重写attribute的对象,优先推荐上面的缓存方法,性能更高也更安全。

内容的提问来源于stack exchange,提问作者Bálint Rétthy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 15:39:05