带有Proxy类的TypeError - TypeError:' set'在代理上:陷阱返回了财产

时间:2017-09-14 20:27:44

标签: javascript node.js proxy

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

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

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. 在开发者重新分配属性时随时抛出错误。每个指定的属性都应该是不可变的。

2 个答案:

答案 0 :(得分:4)

请查看以下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(调用(陷阱,处理程序,«目标,P,V,接收器»))。
  2.   

和同样深奥:

  
      
  1. 如果targetDesc未定义,那么      
        

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

             
          

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

        
      
  2.   

“注释”部分中有相关注释:

  

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

该注释试图将其放入英语中,但它并未指出关键细节,即步骤的时间。第9点是您的setter(<T>)被调用的位。不幸的是,它检查属性是否可写的位是第14点。因此,当执行检查时,属性确实是不可写且不可配置的。

解决此问题的一种方法是通过在<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function() { var List; jQuery.ajax({ url: "http://some ip address/igasp-admin/api.php/um_users?&transform=1", type: "GET", dataType: "json", async: false, success: function (data) { var i; List = data.um_users $('#userData').empty(); for (i in List ) { $('#userData').append('<option value="'+ List[i].last_name + '">' + List[i].first_name + '</option>'); $('#userorgData').append('<option value="">'+ List[i].last_name + '</option>'); } } }); }); </script> </head> <body> <select style="width: 250px;" id="userData"> </select> <select style="width: 250px;" id="userorgData"> </select> </body> </html> 中的trap中查看来配置属性。我并不完全遵循你的用例,所以我不知道这是否是一个可接受的妥协。

我也想知道为什么你需要首先将这些属性设置为不可写。如果始终通过代理访问基础对象,那么您可以完全控制所有configurable: true次调用。我甚至不确定你为什么需要defineProperty而不仅仅是检查属性是否已经在目标对象中。如果你不能假设这些对象将永远通过它们的代理进行访问,那么看起来你已经失去了战斗,因为属性可以在你不知道它的情况下被改变。

这样的东西似乎接近你想要的东西:

set

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

答案 1 :(得分:1)

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

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

  

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

技术上Object.defineProperty是该属性的第一个设置。但这是一个侧面说明。我们可以从MDN的描述中看到writable设置为false禁止我们通过赋值运算符更改属性=是赋值运算符的一个示例。

MDN链接:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty