javascript 代理类的TypeError- TypeError:代理上的“set”:trap返回truish属性

9udxz4iz  于 2023-06-04  发布在  Java
关注(0)|答案(2)|浏览(284)

我在使用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

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

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!
  • 但是当我第一次**设置users属性时,我得到了这个问题的标题错误!*

在我上面的库代码中,mirrorCache是一种检查属性是否已经设置的方法。我想做的,是抛出一个错误,即使我们不是在strict模式下,所以mirrorCache似乎是必要的,以便我自己做簿记。
也许有一个不同的或更好的方法来实现我想实现的目标?以下是我的目标:
1.抛出错误 * 即使不是在严格模式下 *。
1.每当开发人员重新分配属性时抛出错误。每个赋值的属性都应该是不可变的。

vcudknz3

vcudknz31#

请看下面的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.设booleanTrapResult为ToBoolean(Call(trap,handler,«target,P,V,Receiver»))。
以及同样深奥的:
1.如果targetDesc不是undefined,则
a.如果IsDataDescriptor(targetDesc)和targetDesc.Configurable为false,并且targetDesc.Writable为false,则
i.如果SameValue(V,targetDesc.Value)为false,则抛出TypeError异常。
注解部分中有此相关注解:
如果相应的目标对象属性是不可写、不可配置的自有数据属性,则无法将属性的值更改为与相应的目标对象属性的值不同。
这张纸条试图用英语表达出来,但它没有指出关键的细节,也就是步骤的时间。第9点是调用setter(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;
    }
  });
};

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

lnxxn5zx

lnxxn5zx2#

我认为这个问题与传递给Object.defineProperty方法的选项有关。将您的writable选项从false更改为true,我认为您的问题应该得到解决。
MDN对writable属性有以下说明。

writable当且仅当与属性关联的值可以通过赋值运算符更改时为true。默认为false。

因此,从技术上讲,Object.defineProperty是属性的第一个设置。但那是旁注。正如我们从MDN的描述中看到的,writable设置为false不允许我们通过赋值运算符改变属性=就是赋值运算符的一个例子。
MDN链接:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

相关问题