使用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
似乎是必要的,所以我做了我的自己的簿记。
也许有一种不同或更好的方式来实现我想要实现的目标?以下是我的目标:
答案 0 :(得分:4)
请查看以下ECMA规范第9.5.9节:
我很确定你会同意这一点。
我相信两个关键线是:
- 让booleanTrapResult为ToBoolean(调用(陷阱,处理程序,«目标,P,V,接收器»))。
醇>
和同样深奥:
- 如果targetDesc未定义,那么
醇>一个。如果IsDataDescriptor(targetDesc)和targetDesc。[[Configurable]]为false且targetDesc。[[Writable]]为false,则
我。如果SameValue(V,targetDesc。[[Value]])为false,则抛出TypeError异常。
“注释”部分中有相关注释:
如果相应的目标对象属性是不可写,不可配置的自有数据属性,则无法将属性的值更改为与相应目标对象属性的值不同。
该注释试图将其放入英语中,但它并未指出关键细节,即步骤的时间。第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
禁止我们通过赋值运算符更改属性=
是赋值运算符的一个示例。