JavaScript - 代理集与defineProperty

ser*_*0ne 12 javascript proxy object object-properties

我想构建一个检测对象更改的代理:

  • 定义了新属性。
  • 现有属性已更改。

代码示例 1 -defineProperty

const me = {
  name: "Matt"
}

const proxy = new Proxy(me, {
  defineProperty: function(target, key, descriptor) {
    console.log(`Property ${key} defined.`);
    return Object.defineProperty(target, key, descriptor);
  }
});

proxy // { name: 'Matt' }

proxy.name = "Mark";
// Property name defined.
// Mark

proxy.age = 20;
// Property age defined.
// 20
Run Code Online (Sandbox Code Playgroud)

代码示例 1 - 观察

  • proxy有一个属性name,这是我所期望的。
  • 更改name属性告诉我name已定义;不是我所期望的。
  • 定义age属性告诉我age已经定义;正如我所料。

代码示例 2 - 设置

const me = {
  name: "Matt"
}

const proxy = new Proxy(me, {
  defineProperty: function(target, key, descriptor) {
    console.log(`Property ${key} defined.`);
    return Object.defineProperty(target, key, descriptor);
  },
  set: function(target, key, value) {
    console.log(`Property ${key} changed.`);
    return target[key] = value;
  }
});

proxy // { name: 'Matt' }

proxy.name = "Mark";
// Property name changed.
// Mark

proxy.age = 20;
// Property age changed.
// 20
Run Code Online (Sandbox Code Playgroud)

代码示例 2 - 观察

  • proxy有一个属性name,这是我所期望的。
  • 更改name属性告诉我name已更改;正如我所料。
  • 定义age属性告诉我age已更改;不是我所期望的。

问题

  • 为什么要defineProperty捕捉属性变化?
  • 为什么要添加setoverride defineProperty
  • 如何让代理正确捕获defineProperty新属性和set属性更改?

T.J*_*der 10

为什么要defineProperty捕捉属性变化?

因为当您更改数据属性(而不是访问器)时,通过一系列规范步骤,它最终会成为 [[DefineOwnProperty]] 操作。这就是定义更新数据属性的方式:[[Set]] 操作调用OrdinarySet,后者调用OrdinarySetWithOwnDescriptor,后者调用 [[DefineOwnProperty]],从而触发陷阱。

为什么添加set override defineProperty?

因为当你添加set陷阱时,你是在陷阱 [[Set]] 操作并直接在目标上进行,而不是通过代理。所以defineProperty陷阱没有被触发。

如何让代理正确捕获defineProperty新属性并设置属性更改?

defineProperty陷阱需要的时候它被称为更新的属性,当它被称为创建一个属性,它可以用做区分Reflect.getOwnPropertyDescriptorObject.prototype.hasOwnProperty目标。

const me = {
  name: "Matt"
};

const hasOwn = Object.prototype.hasOwnProperty;
const proxy = new Proxy(me, {
  defineProperty(target, key, descriptor) {
    if (hasOwn.call(target, key)) {
      console.log(`Property ${key} set to ${descriptor.value}`);
      return Reflect.defineProperty(target, key, descriptor);
    }
    console.log(`Property ${key} defined.`);
    return Reflect.defineProperty(target, key, descriptor);
  },
  set(target, key, value, receiver) {
    if (!hasOwn.call(target, key)) {
      // Creating a property, let `defineProperty` handle it by
      // passing on the receiver, so the trap is triggered
      return Reflect.set(target, key, value, receiver);
    }
    console.log(`Property ${key} changed to ${value}.`);
    return Reflect.set(target, key, value);
  }
});

proxy; // { name: 'Matt' }

proxy.name = "Mark";
// Shows: Property name changed to Mark.

proxy.age = 20;
// Shows: Property age defined.
Run Code Online (Sandbox Code Playgroud)

这有点随意,但它会让你朝着正确的方向前进。

可以只用一个set陷阱做到这一点,但它不会被任何直接进入 [[DefineOwnProperty]] 而不是通过 [[Set] 的操作触发,例如Object.defineProperty.