Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a Deep Proxy?

How can I create a deep/recursive Proxy?

Specifically, I want to know whenever a property is set or modified anywhere in the object tree.

Here's what I've got so far:

function deepProxy(obj) {
    return new Proxy(obj, {
        set(target, property, value, receiver) {
            console.log('set', property,'=', value);
            if(typeof value === 'object') {
                for(let k of Object.keys(value)) {
                    if(typeof value[k] === 'object') {
                        value[k] = deepProxy(value[k]);
                    }
                }
                value = deepProxy(value);
            }
            target[property] = value;
            return true;
        },
        deleteProperty(target, property) {
            if(Reflect.has(target, property)) {
                let deleted = Reflect.deleteProperty(target, property);
                if(deleted) {
                    console.log('delete', property);
                }
                return deleted;
            }
            return false;
        }
    });
}

And here's my test:

const proxy = deepProxy({});
const baz = {baz: 9, quux: {duck: 6}};

proxy.foo = 5;
proxy.bar = baz;
proxy.bar.baz = 10;
proxy.bar.quux.duck = 999;

baz.quux.duck = 777;
delete proxy.bar;
delete proxy.bar; // should not trigger notifcation -- property was already deleted
baz.quux.duck = 666;  // should not trigger notification -- 'bar' was detached

console.log(proxy);

And the output:

set foo = 5
set bar = { baz: 9, quux: { duck: 6 } }
set baz = 10
set duck = 999
set duck = 777
delete bar
set duck = 666
{ foo: 5 }

As you can see, I've just about got it working, except baz.quux.duck = 666 is triggering the setter even though I've removed it from proxy's object tree. Is there any way to de-proxify baz after the property has been deleted?

like image 750
mpen Avatar asked Apr 03 '17 06:04

mpen


People also ask

How do I configure proxies in deep security manager?

You can configure proxies between various Trend Micro servers and services. In Deep Security Manager, go to Administration > System Settings > Proxies. In the Proxy Servers area, click New > New Proxy Server. In the Name and Description fields, enter a friendly name and description for your proxy.

How do I create a proxy server on my computer?

This wikiHow will teach you how to create a proxy on Windows and Mac computers once your administrator or IT department has set up a proxy server. Open Settings. Press the Windows + I keys or open your Start menu and click the gear icon. Click Network & Internet. It's next to an icon of a gridded globe.

How do I create proxies for my video files?

Select all the footage you want to create proxies for > right-click > and select ‘Proxy’ > ‘Create Proxies’. 2. In this new window, you can select the codec, either H.264 or QuickTime.

What is a proxy server and how does it work?

Creating proxy servers can increase your computer's security as well as save a company's bandwidth since they act as a mediator (middle-man) between your computer and the internet.


2 Answers

Here's a simpler one that does what I think you wanted.

This example allows you to get or set any properties deeply, and calls a change handler on any property (deep or not) to show that it works:

function createOnChangeProxy(onChange, target) {
return new Proxy(target, {
    get(target, property) {
        const item = target[property]
        if (item && typeof item === 'object') return createOnChangeProxy(onChange, item)
        return item
    },
    set(target, property, newValue) {
        target[property] = newValue
        onChange()
        return true
    },
})
}

let changeCount = 0
const o = createOnChangeProxy(() => changeCount++, {})

o.foo = 1
o.bar = 2
o.baz = {}
o.baz.lorem = true
o.baz.yeee = {}
o.baz.yeee.wooo = 12

console.log(changeCount === 6)

const proxy = createOnChangeProxy(() => console.log('change'), {})
const baz = {baz: 9, quux: {duck: 6}};

proxy.foo = 5;
proxy.bar = baz;
proxy.bar.baz = 10;
proxy.bar.quux.duck = 999;

baz.quux.duck = 777;
delete proxy.bar;
delete proxy.bar; // should not trigger notifcation -- property was already deleted
baz.quux.duck = 666;  // should not trigger notification -- 'bar' was detached

console.log(proxy);

In the part that uses your code sample, there are no extra notifications like your comments wanted.

like image 153
trusktr Avatar answered Oct 03 '22 22:10

trusktr


Fixed a bunch of bugs in my original question. I think this works now:

function createDeepProxy(target, handler) {
  const preproxy = new WeakMap();

  function makeHandler(path) {
    return {
      set(target, key, value, receiver) {
        if (value != null && typeof value === 'object') {
          value = proxify(value, [...path, key]);
        }
        target[key] = value;

        if (handler.set) {
          handler.set(target, [...path, key], value, receiver);
        }
        return true;
      },

      deleteProperty(target, key) {
        if (Reflect.has(target, key)) {
          unproxy(target, key);
          let deleted = Reflect.deleteProperty(target, key);
          if (deleted && handler.deleteProperty) {
            handler.deleteProperty(target, [...path, key]);
          }
          return deleted;
        }
        return false;
      }
    }
  }

  function unproxy(obj, key) {
    if (preproxy.has(obj[key])) {
      // console.log('unproxy',key);
      obj[key] = preproxy.get(obj[key]);
      preproxy.delete(obj[key]);
    }

    for (let k of Object.keys(obj[key])) {
      if (obj[key][k] != null && typeof obj[key][k] === 'object') {
        unproxy(obj[key], k);
      }
    }

  }

  function proxify(obj, path) {
    for (let key of Object.keys(obj)) {
      if (obj[key] != null && typeof obj[key] === 'object') {
        obj[key] = proxify(obj[key], [...path, key]);
      }
    }
    let p = new Proxy(obj, makeHandler(path));
    preproxy.set(p, obj);
    return p;
  }

  return proxify(target, []);
}

let obj = {
  foo: 'baz',
}


let proxied = createDeepProxy(obj, {
  set(target, path, value, receiver) {
    console.log('set', path.join('.'), '=', JSON.stringify(value));
  },

  deleteProperty(target, path) {
    console.log('delete', path.join('.'));
  }
});

proxied.foo = 'bar';
proxied.deep = {}
proxied.deep.blue = 'sea';
proxied.null = null;
delete proxied.foo;
delete proxied.deep; // triggers delete on 'deep' but not 'deep.blue'

You can assign full objects to properties and they'll get recursively proxified, and then when you delete them out of the proxied object they'll get deproxied so that you don't get notifications for objects that are no longer part of the object-graph.

I have no idea what'll happen if you create a circular linking. I don't recommend it.

like image 30
mpen Avatar answered Oct 03 '22 23:10

mpen