4

I am trying to print out which nodes are being accessed via the getter by overriding my objects getter with a proxy. I am trying to basically test which parts of this large object are not being used by my application. The problem I am having is being able to add some way of identifying what a getters parents are. Here is what I have so far

function tracePropAccess(obj) {
  return new Proxy(obj, {
    get(target, propKey, receiver) {
      console.log("Get fired on ", propKey);
      return Reflect.get(target, propKey, receiver);
    }
  });
}

const testObj = {
  a: 1,
  b: {
    a: "no",
    b: "yes"
  },
  c: {
    a: "green",
    b: {
      a: "blue",
      b: "orange"
    }
  }
};

const tracer = tracePropAccess(testObj);

tracer.b.a;

tracer.a;

tracer.c.c.a;

This works great in showing me the prop key - however it only the key from the first level. I am not sure how to approach this with this proxy as this single function overrides all the proxies in the provided object. Is there any way to possibly pass in an objects parents/children? Possibly I am approaching this incorrectly as well - so I am looking for any input. Thanks!

2
  • When I run your code it reports the parent: Get fired on b not a. Commented Mar 16, 2018 at 17:28
  • @Mark_M ah you are right - however it only shows the first level, I will update the question. Commented Mar 16, 2018 at 17:33

2 Answers 2

5

You could use the reflection and check if it is an object. If so, return a proxy, if not, return the value.

It does not work on unfinished objects, because the proxy does not know when an object is returned as result or a proxy is to use

Example:

{ foo: { bar: { baz: 42 } } }

and

tracer.foo.bar

does not work, because it should return

{ baz: 42 }

but it returns a new proxy instead, which leads to strange results. The main problem is to know which more keys are coming and with this notation, it is impossible to know what the next or no key is.

function tracePropAccess(obj) {
    return new Proxy(obj, {
        get(target, propKey, receiver) {
            console.log("Get fired on ", propKey);
            var temp = Reflect.get(target, propKey, receiver);
            return temp && typeof temp === 'object'
                ? tracePropAccess(temp)
                : temp;
        }
    });
}

const testObj = { a: 1, b: { a: "no", b: "yes" }, c: { a: "green", b: { a: "blue", b: "orange" } } };

const tracer = tracePropAccess(testObj);

console.log(tracer.b.a);
console.log(tracer.a);
console.log(tracer.c.c.a);

With path

function tracePropAccess(obj, path) {
    path = path || [];
    return new Proxy(obj, {
        get(target, propKey, receiver) {
            var newPath = path.concat(propKey);
            console.log("Get fired on ", newPath);
            var temp = Reflect.get(target, propKey, receiver);
            return temp && typeof temp === 'object'
                ? tracePropAccess(temp, newPath)
                : temp;
        }
    });
}

const testObj = { a: 1, b: { a: "no", b: "yes" }, c: { a: "green", b: { a: "blue", b: "orange" } } };

const tracer = tracePropAccess(testObj);

console.log(tracer.b.a);
console.log(tracer.a);
console.log(tracer.c.c.a);

With path at the end.

function tracePropAccess(obj, path) {
    path = path || [];
    return new Proxy(obj, {
        get(target, propKey, receiver) {
            var temp = Reflect.get(target, propKey, receiver),
                newPath = path.concat(propKey);
            if (temp && typeof temp === 'object') {
                return tracePropAccess(temp, newPath);
            } else {
                console.log("Get fired on ", newPath);
                return temp;
            }
        }
    });
}

const testObj = { a: 1, b: { a: "no", b: "yes" }, c: { a: "green", b: { a: "blue", b: "orange" } } };

const tracer = tracePropAccess(testObj);

console.log(tracer.b.a);
console.log(tracer.a);
console.log(tracer.c.c.a);

Sign up to request clarification or add additional context in comments.

5 Comments

This is great - is there a way to know the entire path though? For instance tracer.c.c.a console logging something like 'fired on c.c.a' ? Thank you!
@ajmajmajma, only once ath the end, or the whole path walking?
Whole path walking - I am trying to see the entire path of every node on this object that has a get being used on it. My goal is to run it on a massive object that I am using currently in my project to see what is not being used. So in this an example I would compile a list of ["b.a", "a","c.c.a"] or something like this.
A think you could possibly do, albeit a little hacky is. Every time you hit a key, create a setImmidiate(collectPathAndSerialize), which has access to the array that can be passed to tracePropAccess(_,_,path = []) with the path.collector being set to the thread id. Now when you go down another path, you clear the old thread and reassign a new thread with its ID assigned to path.collector. Once the path is accessed, and you yield access from your current execution, it can print the path out. You can completely ditch thread logic if you don't mind all the individual paths being printed out
@AP., maybe you add an answer with this suggestion or change my answer, if you like.
0

I encountered the same requirement and came across this question while researching.

This is solvable by creating a factory function for the proxy traps which recursively prepends the current path within the object:

const reporter = (path = []) => ({
  get(target, property) {
    console.log(`getting ${path.concat(property).join('.')}`)
    const value = Reflect.get(target, property)
    if (typeof value === 'object') {
      return new Proxy(value, reporter(path.concat(property)))
    } else {
      return value
    }
  },
})

const o = new Proxy({
  a: 1,
  b: {
    c: 2,
    d: [ 3 ],
  },
}, reporter())

let tmp = o.a   // 1, logs a
tmp = o.b.c     // 2, logs b, b.c
tmp = o.b.d[0]  // 3  logs b, b.d, b.d.0

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.