【问题标题】:TypeError with Proxy class - TypeError: 'set' on proxy: trap returned truish for property代理类的类型错误 - 类型错误:代理上的“设置”:陷阱为属性返回 truish
【发布时间】:2017-09-14 20:27:43
【问题描述】:

我在使用 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. 抛出错误即使不是在严格模式下
  2. 只要开发人员重新分配属性,就会引发错误。每个分配的属性都应该是不可变的。

【问题讨论】:

  • proxy-mcproxy 是一个很酷的小库,当我让它工作时:)

标签: javascript node.js proxy


【解决方案1】:

请看下面的 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 未定义,则

    一个。如果 IsDataDescriptor(targetDesc) 和 targetDesc.[[Configurable]] 为假且 targetDesc.[[Writable]] 为假,则

    我。如果 SameValue(V, targetDesc.[[Value]]) 为 false,则抛出 TypeError 异常。

NOTE 部分有这样的相关评论:

如果对应的目标对象属性是不可写、不可配置的自有数据属性,则不能将属性的值更改为与对应的目标对象属性的值不同。

那个注释试图把它写成英文,但它没有指出关键细节,即步骤的时间安排。第 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;
    }
  });
};

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

【讨论】:

  • 不错的答案,希望明天您的正确答案!
  • return true 在我的情况下也修复了此错误,感谢您的详细说明和解决方案/示例@skirtle
【解决方案2】:

我认为问题与您传递给 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

【讨论】:

  • 是的,也许,但问题是,我希望 writable 为 false,我希望任何属性都只能写入一次。
  • 我对此有点晚了,但是对于阅读此内容的任何人,请检查该属性是否存在并且不要写入它,而不是使用引发错误的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-20
  • 2020-06-24
  • 2017-03-01
  • 1970-01-01
  • 2020-03-29
  • 1970-01-01
  • 2017-12-30
相关资源
最近更新 更多