gpt4 book ai didi

javascript - 数组中对象属性的 getter / setter

转载 作者:行者123 更新时间:2023-11-28 05:34:18 24 4
gpt4 key购买 nike

我有一个Array of Objects

var boxes = [
{ left: 10, top: 20 }, // boxes[0]
{ left: 100, top: 200 } // boxes[1]
];

boxes[0].left = 20; // example of 'set' action


我希望能够为 boxes对象项的每个属性定义通用getter或setter,即使添加了其他项,例如: boxes.push({ left: 30, top: 40 });

这样,最终目标是将 addListener函数隐式附加到每个对象项,从而将侦听器函数绑定到属性的设置器。像这样:

boxes[0].addListener("left", function(new_value){
console.log("left has been changed to "+new_value);
});

boxes[2].addListener("top", ...


假设每个 boxes[i]项目都将像这样填充:

{
left: 10,
top: 20,
set left(val){ /* would trigger every left listener */ },
set top(val){ /* would trigger every top listener */ },
addListener: function(prop, func){ /* ... */ }
}


当然,那些监听器/获取器/设置器必须动态实现(即与 Array.push()函数兼容)。

而“锦上添花”是 boxes数组应与 JSON.stringify()保持可解析,因此:

JSON.stringify(boxes); // would return [{"left":10,"top":20},{"left":100,"top":200}]


我想应该可以通过 ProxiesObject.defineProperty()的某种组合来实现,但是我做不到...

JS大师有什么想法吗?

最佳答案

经过长时间的尝试和错误,终于找到了一个优雅的解决方案。

1.首先,我们用一些通用的Object.prototypeaddListener(prop, listener)函数覆盖removeListener(prop, listener)(每个对象/数组的通用函数),这些函数基本上将给定的侦听器注册到“不可见的” __listeners__数组中:

Object.defineProperty(Object.prototype, "addListener", {
value: function(prop, listener){
if(!("__listeners__" in this)){
Object.defineProperty(this, "__listeners__", {value: {}, writable: true});
}
if(!(prop in this.__listeners__)) this.__listeners__[prop] = [];
if(this.__listeners__[prop].indexOf(listener) == -1){
this.__listeners__[prop].push(listener);
return true;
} else return false;
}
});
Object.defineProperty(Object.prototype, "removeListener", {
value: function(prop, listener){
if("__listeners__" in this && prop in this.__listeners__){
var index = this.__listeners__[prop].indexOf(listener);
if(index > -1){
this.__listeners__[prop].splice(index, 1);
return true;
} else return false;
} else return false;
}
});




2.现在,我们要检测 boxes对象的任何属性/子属性的任何“设置”操作。为此,我们需要设置 Proxies的递归使用。

基本上, Proxy是给定对象/数组的图像。默认情况下,对代理执行的所有操作也会在对象/数组上执行,反之亦然:

var boxes = [
{ left: 10, top: 20 }, // boxes[0]
{ left: 100, top: 200 } // boxes[1]
];

var boxes_proxy = new Proxy(boxes);

boxes_proxy[0].left = 20;

console.log(boxes_proxy[0].left); // 20
console.log(boxes[0].left); // 20


现在的区别是可以检测并覆盖对代理执行的每个动作。对于 boxes_proxy[0].left = 20;,检测到的动作分为两部分:


boxes_proxy[0].:在 boxes上检测到的操作是“获取属性0”,该操作将 boxes[0]作为默认的“获取”处理程序返回。
.left = 20; leftboxes[0]属性设置为20,但由于不是同一对象,因此未检测到。要检测它,我们必须在 boxes[0]上创建另一个代理。


有趣的部分到了。为了解决对象属性的任何子级别(例如 boxes[0].foo.bar[2].font.color)的问题,我们可以创建 recursiveGettergenericSetter函数,使 recursiveGetter返回Proxy而不是其对应的Object / Array。并将任何“设置”动作绑定到触发监听器的 genericSetter函数:

var recursiveGetter = function(real_object, prop){
if(typeof real_object[prop] == "object"){
var prop_proxy = new Proxy(real_object[prop], {
get: recursiveGetter,
set: genericSetter
});
return prop_proxy;
} else return real_object[prop];
}

var genericSetter = function(real_object, prop, value){
real_object[prop] = value;
if("__listeners__" in real_object && prop in real_object.__listeners__){
real_object.__listeners__[prop].forEach(function(fct){ fct(value); });
}
return true;
}


所以现在我们只需要在 new Proxy(boxes)上应用这些getter / setter,它们就会被递归应用:

var boxes_proxy = new Proxy(boxes, { get: recursiveGetter, set: genericSetter });

boxes_proxy[0].addListener("left", function(new_value){
console.log("left has been changed to "+new_value);
});

boxes_proxy[0].left = 20; // "left has been changed to 20"

JSON.stringify(boxes); // [{"left":20,"top":20},{"left":100,"top":200}]


由于添加的对象/功能不可枚举,因此 JSON.stringify仍然适用。

关于javascript - 数组中对象属性的 getter / setter ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39470796/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com