带有代理类的TypeError – types错误:代理中的“set”:陷阱为属性返回truish
使用Proxy类时,出现这个有趣的错误:
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
我有一个库,它创build代理对象属性recursion,其中任何非原始属性是一个代理对象本身,等等等:
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
似乎是必要的,以便我自己做记账。
也许有一个不同的或更好的方法来实现我想要实现的目标? 这是我的目标:
- 即使不在严格模式下也会抛出错误。
- 在开发者重新分配属性时抛出错误。 每个分配的属性应该是不可变的。
看看下面的ECMA规范第9.5.9节:
铆接阅读我相信你会同意。
我相信这两个关键线是:
- 让booleanTrapResult为ToBoolean(调用(trap,handler,«target,P,V,Receiver»))。
和同样深奥的:
- 如果targetDesc不是未定义的,那么
一个。 如果IsDataDescriptor(targetDesc)和targetDesc。[[Configurable]]为false且targetDesc [[Writable]]为false,则
一世。 如果SameValue(V,targetDesc。[[Value]])为false,则引发TypeErrorexception。
注意部分有相关的评论:
如果相应的目标对象属性是不可写的,不可configuration的自有数据属性,则无法将属性的值更改为与相应目标对象属性的值不同。
这个笔记试图把它写成英文,但没有指出关键的细节,这是步骤的时间。 第9点是你的setter( trap
)被调用的位。 不幸的是,它检查该属性是否可写的位是第14点。因此,在执行检查时,该属性确实是不可写和不可configuration的。
解决这个问题的一个办法是通过在你的defineProperty
configurable: true
一个可configurable: true
属性来使属性可configuration。 我不完全遵循你的用例,所以我不能说这是否是一个可接受的妥协。
我也想知道为什么你需要设置这些属性是不可写的。 如果底层对象总是通过它们的代理来访问,那么你可以完全控制所有的调用。 我甚至不确定为什么你需要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对可写属性具有以下描述。
当且仅当与属性相关联的值可能被赋值运算符改变时才为真。 默认为false。
所以在技术上Object.defineProperty
是属性的第一个设置。 但是这是一个方面的说明。 正如我们从MDN的描述中可以看到的那样, writable
设置为false
不允许我们通过赋值操作符来更改属性=
是赋值操作符的示例。
MDN链接: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty