代理类的类型错误 - 类型错误:代理上的"设置":陷阱为属性返回 truish



我在使用代理类时遇到这个有趣的错误:

TypeError: 'set' on proxy: trap returned truish for property 'users' which exists in the proxy target as a non-configurable and non-writable data property with a different value

我有一个递归创建代理对象属性的库,其中任何非原始属性都是代理对象本身,等等:

let mcProxy = function (target) {
const mirrorCache = {};
return new Proxy(target, {
set: function (target, property, value, receiver) {
if (mirrorCache[property]) {
throw new Error(`property ${property} has already been set`);
}
mirrorCache[property] = true;
Object.defineProperty(target, property, {
writable: false,
value: (value && typeof value === 'object') ? mcProxy(value) : value
});
return true;
}
});
};
exports.create = function (val) {
val && assert.equal(typeof val, 'object', 'val must be an object');
return mcProxy(val || {});
};

以上库代码的实际使用情况:

//bash
$ npm install proxy-mcproxy

// nodejs
let McProxy = require('proxy-mcproxy');
let val = McProxy.create();
val.users = [];
val.users = 3; // kaaaboom..error!

但是当我第一次设置用户属性时,我得到了这个问题标题的错误!

在上面的库代码中,mirrorCache是一种检查之前是否已设置属性的方法。我想做的是抛出一个错误,即使我们不处于strict模式,所以mirrorCache似乎是必要的,以便我自己做簿记。

也许有一种不同或更好的方法可以实现我想要实现的目标?以下是我的目标:

  1. 即使不在严格模式下,也会引发错误。
  2. 每当开发人员重新分配属性时引发错误。每个分配的属性都应该是不可变的。

请看以下 ECMA 规范的第 9.5.9 节:

http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver

引人入胜的读物我相信你会同意的。

我认为两个关键线是:

  1. Let booleanTrapResult be ToBoolean(Call(trap, handler, «target, P, V, Receiver»)).

同样深奥的:

  1. 如果 targetDesc 不是未定义的,则

    一个。如果IsDataDescriptor(targetDesc)和targetDesc。[[可配置]] 是假的和目标描述。[[可写]] 是假的,那么

    我。如果 SameValue(V, targetDesc.[[值]])为 false,引发 TypeError 异常。

注释部分有以下相关评论:

如果相应的目标对象属性

是不可写、不可配置的自己的数据属性,则无法将属性的值更改为与相应目标对象属性的值不同。

该注释试图将其翻译成英文,但它没有指出关键细节,即步骤的时间。第9点是你的二传手(trap)被调用的位。不幸的是,它检查属性是否可写的位是第 14 点。因此,在执行检查时,该属性确实是不可写且不可配置的。

解决此问题的一种方法是通过在defineProperty中插入configurable: true来使属性可配置。我不完全遵循您的用例,所以我无法判断这是否是可以接受的妥协。

我也想知道为什么首先需要将这些属性设置为不可写。如果始终通过其代理访问基础对象,则可以完全控制所有set调用。我什至不确定为什么您需要mirrorCache,而不仅仅是检查该属性是否已在目标对象中。如果您不能假设对象将始终通过其代理访问,那么您似乎已经输掉了这场战斗,因为可以在您不知道的情况下更改属性。

像这样的东西似乎很接近你想要的:

let mcProxy = function (target) {
return new Proxy(target, {
set: function (target, property, value) {
if (Object.prototype.hasOwnProperty.call(target, property)) {
throw new Error(`property ${property} has already been set`);
}
target[property] = (value && typeof value === 'object') ? mcProxy(value) : value;
return true;
}
});
};

它需要更多的调整才能正确使用数组,但我不清楚您希望支持哪些数组方法。

我认为问题与您传递给 Object.defineProperty 方法的选项有关。将您的writable选项从false更改为true,我认为您的问题应该得到解决。

MDN 对可写属性有以下描述。

可写true,当且仅当与属性关联的值可以使用赋值运算符更改时。 默认为 false。

所以从技术上讲,Object.defineProperty是该属性的第一个设置。但这是一个旁注。正如我们从 MDN 的描述中看到的writable设置为false不允许我们通过赋值运算符更改属性=这是赋值运算符的一个例子。

MDN 链接: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

最新更新