Javascript 代理类的TypeError-TypeError:';设置';在代理上:陷阱为属性返回truish

Javascript 代理类的TypeError-TypeError:';设置';在代理上:陷阱为属性返回truish,javascript,node.js,proxy,Javascript,Node.js,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

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

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

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

在上面我的库代码中,
mirrorCache
是一种检查属性之前是否已设置的方法。我想做的是抛出一个错误,即使我们没有处于
严格的
模式,所以
镜像缓存
似乎是必要的,这样我就可以自己记账了

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

  • 即使不是在严格模式下也会抛出错误
  • 在开发人员重新分配属性时抛出错误。每个指定的属性都应该是不可变的

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

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

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

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


    MDN链接:

    查看以下ECMA规范第9.5.9节:

    一本引人入胜的书我相信你会同意的

    我认为这两条关键线是:

  • 让booleanTrapResult成为ToBoolean(调用(trap,handler,«target,P,V,Receiver»))
  • 同样深奥的是:

  • 如果targetDesc未定义,则 a。如果IsDataDescriptor(targetDesc)和targetDesc.[Configurable]]为false,而targetDesc.[Writable]]为false,则

    一,。如果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;
        }
      });
    };
    

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

    可能是的,但问题是,我希望可写为false,我希望任何属性只写一次。我做得很晚,但对于阅读本文的人来说,检查该属性是否存在,不要对其进行写入,而要使用引发错误的解决方案。proxy-mcproxy是一个很酷的小库,当我让它工作时:)回答不错,我希望你是对的,明天会验证
    return true
    也修复了我的案例中的此错误,感谢您的详细阐述和解决方案/example@skillle
    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;
        }
      });
    };