0

I'm experimenting with Proxies in ES6, was trying to create an infinitely chainable Proxy on an object(got from https://jsonplaceholder.typicode.com/users) which should return empty{} if the prop is not found.

I tried to implement this functionality up to 2nd level(e.g, user.address.geo). EDIT: Updated code for check type of prop value

let users = [{
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
        "street": "Kulas Light",
        "suite": "Apt. 556",
        "city": "Gwenborough",
        "zipcode": "92998-3874"
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org"
},
{
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "email": "Shanna@melissa.tv",
    "address": {
        "street": "Victor Plains",
        "suite": "Suite 879",
        "city": "Wisokyburgh",
        "zipcode": "90566-7771"
    },
    "phone": "010-692-6593 x09125",
    "website": "anastasia.net"
}];

I came up with below code

    var handler = {
    get: function (target, name) {
        return name in target ?
            target[name] : {};
    }
};

let pusers = users.map(item => {
    let pitem = new Proxy(item, handler);
    Reflect.ownKeys(pitem).map(prop => {
        pitem[prop] = (typeof pitem[prop] == 'object') ? new Proxy(pitem[prop], handler) : pitem[prop];
    })
    return pitem;
});

pusers.map(u => {
    console.log(u.address);
    console.log(u.contact.city)
});

Output for this code is not appealing, it is returning undefined instead of an empty{} object

{ street: 'Kulas Light',
  suite: 'Apt. 556',
  city: 'Gwenborough',
  zipcode: '92998-3874' }
undefined

I did this few times and still getting the same result. Am I missing something?

NikhilGoud
  • 574
  • 1
  • 5
  • 21
  • 1
    [There is no such thing as a "JSON Object"](http://benalman.com/news/2010/03/theres-no-such-thing-as-a-json/) – Felix Kling Mar 16 '17 at 17:27
  • `checkJSON` - what? That looks horribly convoluted and it's not clear at all what the function is supposed to do. – Bergi Mar 16 '17 at 17:38
  • to Check whether the value is of type object: I got that from http://stackoverflow.com/questions/4295386/how-can-i-check-if-a-value-is-a-json-object – NikhilGoud Mar 16 '17 at 17:59
  • Shudder. Don't do that. You don't have any JSON around, so you don't need to stringify/parse anything. Just test `typeof x == "object"`. – Bergi Mar 16 '17 at 18:03
  • Updated the code, still not able to acheive the required functionality – NikhilGoud Mar 17 '17 at 11:32

1 Answers1

3

You don't want to use map to wrap only the elements of your array in proxies. Instead, you want your get handler that returns properties to wrap all objects:

const handler = {
    get(target, name) {
        const v = target[name];
        return typeof v == "object" ? new Proxy(v, handler) : v;
    }
};
let pusers = new Proxy(users, handler);

Every nested object access will go through the handler now, including things like pusers[0].contact.city.

Now you just need to add the default values for non-existing properties, and I guess you will want to store them on the respective target to allow mutations:

const handler = {
    get(target, name) {
        const v = name in target ? target[name] : (target[name] = {});
        return typeof v == "object" ? new Proxy(v, handler) : v;
    }
};
let pusers = new Proxy(users, handler);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Shoot, I did not really look into changing the handler, just worked on assigning the handler to the obj array instead which is obviously wrong. thanks for the detailed explanation. One thing, So here the proxy will be applied dynamically right? while accessing the prop. – NikhilGoud Mar 17 '17 at 13:17
  • Yes, exactly. Notice that this implies `pusers[0] != pusers[0]` as they are different proxy instances created from different handler calls. If you want to avoid that, you'll need to store the proxies themselves somewhere (maybe a `WeakMap`). – Bergi Mar 17 '17 at 13:27