在 proto 代理之上分配一个 属性

Assign a property above proto proxy

以下代码包含对象 __proto__ 中带有 get 陷阱的代理。当从对象中获取一些属性时,根据js逻辑,只有当对象本身不包含对应的属性时才会调用trap。因此在赋值后 属性 出现在对象中并且不会调用 get 陷阱。它完全符合我的要求。

var x = Object.create(new Proxy({}, {
  get(obj, key) {
    if (typeof key !== 'symbol') {
      console.log('Reading a nonexisting property: ' + key);
    }
  }
}));

var temp;
console.log(1, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(2, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

temp = x.a;
console.log(3, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

x.a = 12;
console.log(4, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(5, Object.prototype.hasOwnProperty.call(x, 'a'), temp);
.as-console-wrapper.as-console-wrapper { max-height: 100vh }

现在我要添加一个 set 陷阱:

var x = Object.create(new Proxy({}, {
  get(obj, key) {
    if (typeof key !== 'symbol') {
      console.log('Reading a nonexisting property: ' + key);
    }
  },
  set(obj, key, val, receiver) {
    console.log('Assigning a property: ' + key);
    Reflect.set(obj, key, val); // Inside of proxy, not outside
    //Reflect.set(receiver, key, val); // Infinite recursion
    return true;
  }
}));

var temp;
console.log(1, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(2, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

temp = x.a;
console.log(3, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

x.a = 12;
console.log(4, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a;
console.log(5, Object.prototype.hasOwnProperty.call(x, 'a'), temp);
.as-console-wrapper.as-console-wrapper { max-height: 100vh }

问题是我无法直接向对象写入 属性。 属性 要么写入被代理 包装的对象 而不是 包装代理 的对象,要么 [= 的无限递归17=]陷阱发生。

所以我想获得与第一个片段相同的输出,但换行 Assigning a property: a

PS: Same qusetion in Russian.

要实际创建 属性,您必须使用 Object.defineProperty(或 Reflect.defineProperty,如果您愿意)。只需通过赋值或 Reflect.set 设置 属性 确实会遍历原型链,因此当您对原型(或代理陷阱)上的 setter 中的对象执行此操作时,您总是确实得到递归。

你可以使用

new Proxy({}, {
  get(target, key) {
    if (typeof key !== 'symbol') {
      console.log('Reading a nonexisting property: ' + key);
    }
  },
  set(target, key, val, receiver) {
    console.log('Assigning a property: ' + key);
    return Reflect.defineProperty(receiver, key, {
      value: val,
      writable: true,
      enumerable: true,
      configurable: true
    });
  }
});

完整代码:

var x = Object.create(new Proxy({}, {
  get(obj, key) {
    if (typeof key !== 'symbol') {
      console.log('Reading a nonexisting property: ' + key);
    }
  },
  set(obj, key, val, receiver) {
    console.log('Assigning a nonexisting property: ' + key);

    return Reflect.defineProperty(receiver, key, {
      value: val,
      writable: true,
      enumerable: true,
      configurable: true
    });
  }
}));

var temp;
console.log(1, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a; // get trap
console.log(2, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

temp = x.a; // get trap
console.log(3, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

x.a = 12;   // set trap creates a property and sets it
console.log(4, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a; // direct read - no traps
console.log(5, Object.prototype.hasOwnProperty.call(x, 'a'), temp);

x.a = 42;   // direct write - no traps
console.log(6, Object.prototype.hasOwnProperty.call(x, 'a'));

temp = x.a; // direct read - no traps
console.log(7, Object.prototype.hasOwnProperty.call(x, 'a'), temp);
.as-console-wrapper.as-console-wrapper { max-height: 100vh }