带有代理类的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似乎是必要的,以便我自己做记账。

也许有一个不同的或更好的方法来实现我想要实现的目标? 这是我的目标:

  1. 即使不在严格模式下也会抛出错误。
  2. 在开发者重新分配属性时抛出错误。 每个分配的属性应该是不可变的。

看看下面的ECMA规范第9.5.9节:

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

铆接阅读我相信你会同意。

我相信这两个关键线是:

  1. 让booleanTrapResult为ToBoolean(调用(trap,handler,«target,P,V,Receiver»))。

和同样深奥的:

  1. 如果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