观察我们无法使用代理修改的对象

时间:2019-01-03 18:36:42

标签: javascript proxy

我想使用代理(我没有制作的游戏中的对象)观察一个对象。我发现这是另一个问题的答案:

var proxied = new Proxy(foo, {
    get: function(target, prop) {
        console.log({
            type: "get",
            target,
            prop
        })
        return Reflect.get(target, prop)
    },
    set: function(target, prop, value) {
        console.log({
            type: "set",
            target,
            prop,
            value
        })
        return Reflect.set(target, prop, value)
    }
})

如果更改它,我必须做proxied.bar = "Hello world"。但是我没有改变,游戏却改变了。而且它不会像这样(它确实foo.bar = ...,他们不使用代理...)那么,我该怎么做才能观察数组? 谢谢。

1 个答案:

答案 0 :(得分:0)

没有太多选择可以观察对象是否已更改。以前我们可以使用Object.observe()Object.watch(),但是这两个都已被弃用,不再可靠。

代理是唯一的选择,但是显然游戏必须使用代理对象才能使陷阱被命中。

就操作原始foo对象而言,我并不完全知道您能做什么,但是将proxied设置为指向它的对象,而不是创建您的foo对象自己代理的自我。换句话说,

foo = new Proxy(foo, {
    get: function(target, prop) {
        console.log({
            type: "get",
            target,
            prop
        })
        return Reflect.get(target, prop)
    },
    set: function(target, prop, value) {
        console.log({
            type: "set",
            target,
            prop,
            value
        })
        return Reflect.set(target, prop, value)
    }
})

如果您可以像这样直接操纵游戏对象,则可以在getset陷阱中传递自己的回调函数,并观察该对象的任何变化。

您可能无法重新分配foo(如果它是用const声明的)。在这种情况下,可以解决此问题,但是它有点笨拙且不像上述方法那样同步。

基本上,您可以创建一个函数,该函数在foo对象中创建条目的缓存,并以给定的间隔重新检查foo是否有任何更改,并执行您指定的任何回调逻辑。我不能太具体,因为实现细节确实取决于foo的结构( eg ,其嵌套性以及它可能包含的对象的类型-它们是日期,函数引用,或许多其他类型的对象)。

基本实现可能如下所示:

foo = { ... } // whatever kind of object foo is

function observeFoo() {
    const entries = Object.entries(foo);

    // initialize cache if we don't have one
    if (!observeFoo.cache) observeFoo.cache = {};

    // begin checking keys in cache
    entries.forEach(entry => {
        const [ key, value ] = entry;
        if (observeFoo.cache[key] !== value) {
            // do something regarding this change
        }

        // update cache
        observeFoo.cache[key] = value;
   });
}

setInterval(observeFoo, 1000);