vanowm
vanowm

Reputation: 10201

Proxy object - access target from within a getter property?

I have a simple proxy object:

const myProxy = new Proxy(
  /* myTarget */
  {
    test: 2,
  },
  /* myHandler */
  {
    get(target, prop, proxy)
    {
      if (prop in this && this[prop] instanceof Function)
        return this[prop](target); //getter

      return target[prop]; //value
    },
    
    myGetter(target)
    {
      return ++target.test
    }
  }
);

console.log("test before", myProxy.test);
console.log("myGetter", myProxy.myGetter);
console.log("test after", myProxy.test);

Is there a way make myGetter property as a true getter? My attempt failed, because I don't know how to access myTarget from within the getter:

const myProxy = new Proxy(
  /* myTarget */
  {
    test: 2,
  },
  /* myHandler */
  {
    get(target, prop, proxy)
    {
      if (prop in this)
        return this[prop]; //getter

      return target[prop]; //value
    },
    
    get myGetter()
    {
      return ++target.test; //target undefined
    }
  }
);

console.log("test before", myProxy.test);
console.log("blah", myProxy.myGetter);
console.log("test after", myProxy.test);

The reason why I need this is because I have multiple functions inside the handler and I need to determine which one needs to be executed within handler itself (myGetter), or should be returned as function reference (myFunc), where it can also accept additional data as arguments, so I'm using prop instanceof Function to check if it's a function or not:

const myProxy = new Proxy(
  /* myTarget */
  {
    test: 2,
  },
  /* myHandler */
  {
    get(target, prop, proxy)
    {
      if (prop in this)
      {
        if (this[prop] instanceof Function)
          return (args) => this[prop](target, args); //function

        return this[prop]; //getter
      }
      return target[prop]; //value
    },
    
    get myGetter()
    {
      return ++target.test; //target undefined
    },

    myFunc(target, value)
    {
      return target.test * value;
    }
  }
);

console.log("myFunc", myProxy.myFunc(3));
console.log("test before", myProxy.test);
console.log("myGetter", myProxy.myGetter);
console.log("test after", myProxy.test);

I could think of several work arounds, for example hard code names of functions:

const myProxy = new Proxy(
  /* myTarget */
  {
    test: 2,
  },
  /* myHandler */
  {
    get(target, prop, proxy)
    {
      if (prop in this)
      {
        if (["myFunc"].includes(prop)) // function
          return (args) => this[prop](target, args);

        return this[prop](target); // getter
      }
      return target[prop];
    },
    
    myGetter(target)
    {
      return ++target.test
    },

    myFunc(target, value)
    {
      return target.test * value;
    }
  }
);

console.log("myFunc", myProxy.myFunc(3));
console.log("test before", myProxy.test);
console.log("myGetter", myProxy.myGetter);
console.log("test after", myProxy.test);

Or separate functions into groups:

const myProxy = new Proxy(
  /* myTarget */
  {
    test: 2,
  },
  /* myHandler */
  {
    get(target, prop, proxy)
    {
      if (prop in this._myFuncs)
        return (args) => this._myFuncs[prop](target, args);

      if (prop in this._myGetters)
        return this._myGetters[prop](target);

      return target[prop];
    },
    
    _myGetters:
    {
      myGetter(target)
      {
        return ++target.test
      },
    },

    _myFuncs:
    {
      myFunc(target, value)
      {
        return target.test * value;
      },
    }
  }
);

console.log("myFunc", myProxy.myFunc(3));
console.log("test before", myProxy.test);
console.log("myGetter", myProxy.myGetter);
console.log("test after", myProxy.test);

For education purpose any suggestions how to make true getter to work in Proxy?

Upvotes: 0

Views: 936

Answers (1)

CertainPerformance
CertainPerformance

Reputation: 370789

I don't think the handler object should have nonstandard properties on it - it'd make more sense for it to only have the traps that it's expected to have. To have collection of properties for traps, either hard-code those properties (which is inflexible and not so nice) or use a separate object structure for those properties. With a separate object in the proxy closure, checking whether the property exists on the object and then .calling it with target if it's a getter is easy.

const makeProxy = (target, customProperties) => {
  return new Proxy(
    target, {
      get(target, prop, proxy) {
        if (prop in customProperties) {
          // Invoke getter if it exists on customProperties
          const { get } = Object.getOwnPropertyDescriptor(customProperties, prop);
          if (get) {
            return get.call(target);
          }
        }
        // Otherwise return original object value or getter
        return target[prop];
      },
    }
  );
};

const proxy = makeProxy({
  test: 2,
}, {
  get myGetter() {
    return ++this.test
  }
});

console.log("test before", proxy.test);
console.log("blah", proxy.myGetter);
console.log("test after", proxy.test);

Upvotes: 1

Related Questions