JavaScript - 代理集与 defineProperty
JavaScript - Proxy set vs. defineProperty
我想构建一个检测对象更改的代理:
- 定义了新属性。
- 现有属性已更改。
代码示例 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
代码示例 1 - 观察
proxy
有一个 属性 name
,这正是我所期望的。
- 更改
name
属性 告诉我 name
已定义; 不是我所期望的。
- Defining the
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
代码示例 2 - 观察
proxy
有一个 属性 name
,这正是我所期望的。
- 更改
name
属性 告诉我 name
已更改;正如我所料。
- 定义
age
属性 告诉我 age
已更改; 不是我所期望的。
问题
- 为什么
defineProperty
捕获 属性 变化?
- 为什么添加
set
会覆盖 defineProperty
?
- 如何让代理正确捕获新属性的
defineProperty
和 属性 更改的 set
?
Why does defineProperty
catch property changes?
因为当您更改数据 属性(与访问器相反)时,通过一系列规范步骤,它最终成为 [[DefineOwnProperty]] 操作。这就是更新数据 属性 的定义方式: [[Set]] operation calls OrdinarySet which calls OrdinarySetWithOwnDescriptor 调用 [[DefineOwnProperty]],触发陷阱。
Why does the addition of set override defineProperty?
因为当您添加 set
陷阱时,您将捕获 [[Set]] 操作并直接在目标上执行,而不是通过代理。所以 defineProperty
陷阱没有触发。
How do I get the proxy to correctly trap defineProperty
for new properties and set for property changes?
defineProperty
陷阱需要区分何时调用它来更新 属性 和何时调用它来创建 属性,它可以通过使用 Reflect.getOwnPropertyDescriptor
或 Object.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.
这有点即兴,但它会让你朝着正确的方向前进。
你 可以 只用一个 set
陷阱来做到这一点,但这不会被直接到 [[DefineOwnProperty]] 的任何操作触发,而不是通过 [[Set],例如 Object.defineProperty
.
我想构建一个检测对象更改的代理:
- 定义了新属性。
- 现有属性已更改。
代码示例 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
代码示例 1 - 观察
proxy
有一个 属性name
,这正是我所期望的。- 更改
name
属性 告诉我name
已定义; 不是我所期望的。 - Defining the
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
代码示例 2 - 观察
proxy
有一个 属性name
,这正是我所期望的。- 更改
name
属性 告诉我name
已更改;正如我所料。 - 定义
age
属性 告诉我age
已更改; 不是我所期望的。
问题
- 为什么
defineProperty
捕获 属性 变化? - 为什么添加
set
会覆盖defineProperty
? - 如何让代理正确捕获新属性的
defineProperty
和 属性 更改的set
?
Why does
defineProperty
catch property changes?
因为当您更改数据 属性(与访问器相反)时,通过一系列规范步骤,它最终成为 [[DefineOwnProperty]] 操作。这就是更新数据 属性 的定义方式: [[Set]] operation calls OrdinarySet which calls OrdinarySetWithOwnDescriptor 调用 [[DefineOwnProperty]],触发陷阱。
Why does the addition of set override defineProperty?
因为当您添加 set
陷阱时,您将捕获 [[Set]] 操作并直接在目标上执行,而不是通过代理。所以 defineProperty
陷阱没有触发。
How do I get the proxy to correctly trap
defineProperty
for new properties and set for property changes?
defineProperty
陷阱需要区分何时调用它来更新 属性 和何时调用它来创建 属性,它可以通过使用 Reflect.getOwnPropertyDescriptor
或 Object.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.
这有点即兴,但它会让你朝着正确的方向前进。
你 可以 只用一个 set
陷阱来做到这一点,但这不会被直接到 [[DefineOwnProperty]] 的任何操作触发,而不是通过 [[Set],例如 Object.defineProperty
.