Skip to content Skip to sidebar Skip to footer

Determining If Get Handler In Proxy Object Is Handling A Function Call

I currently have a Proxy object that I want to capture property calls to if the property is not defined. A basic version of my code would be something like this. var a = new Proxy(

Solution 1:

It's possible in a very hacky way. We return a function if the property is undefined. If this function is called, then we know the user was trying to call the property as a function. If it never is, it was called as a property. To check if the function was called, we take advantage of the fact that a Promise's callback is called in the next iteration of the event loop. This means that we won't know if it's a property or not until later, as the user needs a chance to call the function first (as our code is a getter).

One drawback of this method is that the value returned from the object will be the new function, not undefined, if the user was expecting a property. Also this won't work for you if you need the result right away and can't wait until the next event loop iteration.

const obj = {
  func: undefined,
  realFunc: () =>"Real Func Called",
  prop: undefined,
  realProp: true
};

const handlers = {
  get: (target, name) => {
    const prop = target[name];
    if (prop != null) { return prop; }

    let isProp = true;
    Promise.resolve().then(() => {
      if (isProp) {
        console.log(`Undefined ${name} is Prop`)
      } else {
        console.log(`Undefined ${name} is Func`);
      }
    });
    returnnewProxy(()=>{}, {
      get: handlers.get,
      apply: () => {
        isProp = false;
        returnnewProxy(()=>{}, handlers);
      }
    });
  }
};

const proxied = newProxy(obj, handlers);

let res = proxied.func();
res = proxied.func;
res = proxied.prop;
res = proxied.realFunc();
console.log(`realFunc: ${res}`);
res = proxied.realProp;
console.log(`realProp: ${res}`);
proxied.propC1.funcC2().propC3.funcC4().funcC5();

Solution 2:

You can't know ahead of time whether it's a call expression or just a member expression, but you can deal with both situations simultaneously.

By returning a proxy targeting a deep clone of the original property that reflects all but two trap handlers to the original property, you can either chain or invoke each member expression.

The catch is that the proxy target also needs to be callable so that the handler.apply trap does not throw a TypeError:

functionwatch(value, name) {
  // create handler for proxyconst handler = newProxy({
    apply (target, thisArg, argsList) {
      // something was invoked, so return custom arrayreturn [value, name, receiver, argsList];
    },
    get (target, property) {
      // a property was accessed, so wrap it in a proxy if possibleconst {
        writable,
        configurable
      } = Object.getOwnPropertyDescriptor(target, property) || { configurable: true };
      return writable || configurable 
        ? watch(value === object ? value[property] : undefined, property)
        : target[property];
    }
  }, {
    get (handler, trap) {
      if (trap in handler) {
        return handler[trap];
      }
      // reflect intercepted traps as if operating on original valuereturn(target, ...args) =>Reflect[trap].call(handler, value, ...args);
    }
  });
  
  // coerce to object if value is primitiveconst object = Object(value);
  // create callable target without any own propertiesconsttarget = () => {};
  delete target.length;
  delete target.name;
  // set target to deep clone of objectObject.setPrototypeOf(
    Object.defineProperties(target, Object.getOwnPropertyDescriptors(object)),
    Object.getPrototypeOf(object)
  );
  // create proxy of targetconst receiver = newProxy(target, handler);
  
  return receiver;
}

var a = watch({ b: { c: 'string' }, d: 5 }, 'a');

console.log(a('foo', 'bar'));
console.log(a.b());
console.log(a.b.c());
console.log(a.d('hello', 'world'));
console.log(a.f());
console.log(a.f.test());
Open Developer Tools to view Console.

The Stack Snippets Console attempts to stringify the receiver in a weird way that throws a TypeError, but in the native console and Node.js it works fine.

Try it online!

Solution 3:

Would the typeof operator work for you?

For example:

if(typeof(a) === "function")
{
    ...
}
else
{
    ...
}

Post a Comment for "Determining If Get Handler In Proxy Object Is Handling A Function Call"