确定Proxy对象中的get处理程序是否正在处理函数调用

时间:2017-06-08 16:37:08

标签: javascript

我目前有一个Proxy对象,如果未定义属性,我想捕获属性调用。 我的代码的基本版本就是这样的。

var a = new Proxy({}, {
    get: function(target, name, receiver) {
        if (target in name) {
            return target[name];
        } else {    
            function a() {
                return arguments;
            }
            var args = a();
            return [target, name, receiver, args];
        }
    }
});

对此处的属性调用(即:a.b; a.c()等)应该返回属性调用的目标,名称,接收者和参数。

然而,我想要解决的问题要求我知道属性调用是属性还是函数,这样我可以对每个属性应用不同的处理。检查参数对象的长度不起作用,因为调用a.c()将产生与a.b类似的长度0,因此它将被视为普通属性而不是方法。

因此,是否有办法确定尝试访问的属性是否被调用为函数。

更新:我应该澄清,如果访问的属性/方法未定义,以及现有属性/方法,此方法需要工作。

3 个答案:

答案 0 :(得分:2)

这可能是一种非常黑客的方式。如果属性为undefined,我们返回一个函数。如果调用此函数,则我们知道用户正在尝试将该属性作为函数调用。如果它永远不会,它被称为财产。为了检查函数是否被调用,我们利用了在事件循环的下一次迭代中调用Promise的回调这一事实。这意味着我们不会知道它是否是属性,直到以后,因为用户需要有机会首先调用该函数(因为我们的代码是一个getter)。

此方法的一个缺点是,如果用户期望属性,则从对象返回的值将是新函数,而不是undefined。如果您需要立即获得结果并且不能等到下一个事件循环迭代,这对您不起作用。

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`);
      }
    });
    return new Proxy(()=>{}, {
      get: handlers.get,
      apply: () => {
        isProp = false;
        return new Proxy(()=>{}, handlers);
      }
    });
  }
};

const proxied = new Proxy(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();

答案 1 :(得分:1)

您无法提前知道它是一个呼叫表达式还是一个成员表达式,但您可以同时处理这两种情况。

通过返回一个代理原始属性的深层克隆的代理,该代理反映了除原来的两个陷阱处理程序以外的所有陷阱处理程序,您可以链接或调用每个成员表达式。

问题是代理目标也需要可调用,以便handler.apply陷阱不会抛出TypeError



function watch(value, name) {
  // create handler for proxy
  const handler = new Proxy({
    apply (target, thisArg, argsList) {
      // something was invoked, so return custom array
      return [value, name, receiver, argsList];
    },
    get (target, property) {
      // a property was accessed, so wrap it in a proxy if possible
      const {
        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 value
      return (target, ...args) => Reflect[trap].call(handler, value, ...args);
    }
  });
  
  // coerce to object if value is primitive
  const object = Object(value);
  // create callable target without any own properties
  const target = () => {};
  delete target.length;
  delete target.name;
  // set target to deep clone of object
  Object.setPrototypeOf(
    Object.defineProperties(target, Object.getOwnPropertyDescriptors(object)),
    Object.getPrototypeOf(object)
  );
  // create proxy of target
  const receiver = new Proxy(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.




Stack Snippets Console尝试以一种引发receiver的奇怪方式对TypeError进行字符串化,但在本机控制台和Node.js中它可以正常工作。

CSS table column autowidth

答案 2 :(得分:0)

typeof运算符会为你工作吗?

例如:

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