代理类的类型错误 - 类型错误:代理上的“设置”:陷阱为属性返回了真值

Ale*_*lls 4 javascript proxy 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
Run Code Online (Sandbox Code Playgroud)

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

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 || {});
};
Run Code Online (Sandbox Code Playgroud)

上述库代码的实际使用:

//bash
$ npm install proxy-mcproxy
Run Code Online (Sandbox Code Playgroud)
 // nodejs
 let McProxy = require('proxy-mcproxy');
 let val = McProxy.create();
 val.users = [];
 val.users = 3; // kaaaboom..error!
Run Code Online (Sandbox Code Playgroud)

但是当我一次设置用户属性时,这个问题的标题出现错误!

在我上面的库代码中,mirrorCache是一种检查属性是否先前已设置的方法。想要我想要做的,就是抛出一个错误,即使我们不在strict模式下,所以mirrorCache似乎有必要让我自己做簿记。

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

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

ski*_*tle 6

查看以下内容,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(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。因此,在执行检查时,属性确实不可写且不可配置。

解决此问题的一种方法是通过configurable: truedefineProperty. 我并不完全遵循您的用例,因此我无法确定这是否是可以接受的妥协。

我还想知道为什么您首先需要将这些属性设置为不可写。如果始终通过其代理访问底层对象,则您可以完全控制所有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;
    }
  });
};
Run Code Online (Sandbox Code Playgroud)

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