数组

时间:2016-09-13 13:02:21

标签: javascript

我有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函数附加到每个对象项,该对象项将侦听器函数绑定到属性的setter。像这样:

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){ /* ... */ }
}

当然,那些侦听器/ getter / setter必须动态实现(即与Array.push()功能兼容)。

并且"锦上添花"是boxes数组应该与JSON.stringify()保持一致,所以:

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

我想应该可以使用ProxiesObject.defineProperty()的某种组合,但我无法做到......

来自JS大师的任何想法?

1 个答案:

答案 0 :(得分:0)

最后,经过长时间的试验和错误,我找到了一个优雅的解决方案。

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是给定对象/数组的图像。默认情况下,代理上的每个操作也都在Object / Array上进行,反之亦然:

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;的情况下,检测到的操作分为两部分:

  1. boxes_proxy[0].boxes上检测到的操作是"获取属性0",返回boxes[0]作为默认值" get&#34 ;处理程序。

  2. .left = 20; left的{​​{1}}属性设置为20,但未检测到它,因为它不是同一个对象。要检测它,我们必须在boxes[0]上创建另一个代理。

  3. 这是有趣的部分。要为任何子级别的对象属性(例如boxes[0])解决此问题,我们可以创建boxes[0].foo.bar[2].font.colorrecursiveGetter函数,方式是genericSetter返回代理而不是相应的Object / Array并绑定任何" set"对触发侦听器的recursiveGetter函数的操作:

    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; } 上应用这些getter / setter,它们将以递归方式应用:

    new Proxy(boxes)

    由于添加的对象/函数不可枚举,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}] 仍然适用。