是否可以更改代理的目标?

时间:2016-07-14 01:17:08

标签: javascript xmlhttprequest ecmascript-6

我有一个实现XMLHttpRequest接口的类。根据传递给open()的URL,我可以确定是使用默认的XMLHttpRequest还是我的自定义实现。我的想法是使用代理来执行此操作:

let xhr = new XHRProxy();
xhr.open('GET', 'http://blah'); // Decide here depending on URL

我使用ES6代理进行了一些测试,看起来很有希望,但不幸的是,构建代理后无法修改代理目标:

var foo = {
    name() {
        return "foo";
    }
};
var bar = {
    name() {
        return "bar";
    }
}
var handler = {
    get(target, property, receiver) {
        if (property === "switchToBar") {
            // FIXME: This doesn't work because a Proxy's target is not exposed AFAIK
            receiver.target = bar;
            return function() {};
        } else {
            return target[property];
        }
    }
}
var proxy = new Proxy(foo, handler);
console.log(proxy.name()); // foo
proxy.switchToBar();
console.log(proxy.name()); // foo  :(

我认为我可以通过不设置目标来完成我想要的 - 而是定义所有陷阱以委托给所需的对象 - 但我希望有一个更简单的解决方案。

4 个答案:

答案 0 :(得分:10)

这里是“定义委托给所需对象的所有陷阱”

(function () {
  let mutableTarget;
  let mutableHandler;

  function setTarget(target) {
    if (!(target instanceof Object)) {
      throw new Error(`Target "${target}" is not an object`);
    }
    mutableTarget = target;
  }

  function setHandler(handler) {
    Object.keys(handler).forEach(key => {
      const value = handler[key];

      if (typeof value !== 'function') {
        throw new Error(`Trap "${key}: ${value}" is not a function`);
      }

      if (!Reflect[key]) {
        throw new Error(`Trap "${key}: ${value}" is not a valid trap`);
      }
    });
    mutableHandler = handler;
  }

  function mutableProxyFactory() {
    setTarget(() => {});
    setHandler(Reflect);

    // Dynamically forward all the traps to the associated methods on the mutable handler
    const handler = new Proxy({}, {
      get(target, property) {
        return (...args) => mutableHandler[property].apply(null, [mutableTarget, ...args.slice(1)]);
      }
    });

    return {
      setTarget,
      setHandler,
      getTarget() {
        return mutableTarget;
      },
      getHandler() {
        return mutableHandler;
      },
      proxy: new Proxy(mutableTarget, handler)
    };
  }

  window.mutableProxyFactory = mutableProxyFactory;
})();

const { 
  proxy, 
  setTarget 
} = mutableProxyFactory();

setTarget(() => 0);
console.log(`returns: ${proxy()}`);

setTarget({ val: 1 });
console.log(`val is: ${proxy.val}`);

setTarget({ val: 2 });
console.log(`val is: ${proxy.val}`);

setTarget(() => 3);
console.log(`returns: ${proxy()}`);

我觉得必须有一些理由不支持开箱即用,但我没有足够的信息对此进行评论。

经过一段时间的黑客攻击后,我发现了一些事情。无论如何,代理构造函数调用的原始目标似乎都被视为代理身份的一部分。在调用代理时,将原始目标设置为普通对象并将目标交换到函数稍后会引发错误。这肯定有一些粗糙的边缘,所以请谨慎使用。

答案 1 :(得分:4)

  

是否可以更改代理的目标?

不,这是不可能的。代理处理程序已经是一个非常通用的接口,通过定义所有陷阱来将操作转发到不同的处理程序,这很容易实现。这就是没有额外方法来改变目标的原因,界面保持最小化。通过不使目标更改,也保留了代理的形状(例如,它是可调用的还是数组)。

答案 2 :(得分:2)

这个怎么样?我们不是直接使用barfoo目标,而是使用一个框作为目标,并将barvar foo = { name() { return "foo"; } }; var bar = { name() { return "bar"; } }; var handler = { get(target, property, receiver) { if (property === "switchToBar") { target.content = bar; return function() {}; } else { return target.content[property]; } } }; var box = {content: foo}; var proxy = new Proxy(box, handler); console.log(proxy.name()); // foo // Switch over to bar by calling the function proxy.switchToBar(); // Or, we could do the switch from out here box.content = bar; // Either way, we get the same result console.log(proxy.name()); // bar 放入框中。

content

在这种情况下,我们的框是一个属性为mydic = { "aiStandard" : ["pSphere1.f[1:20]", "pSphere1.f[22:28]", "pSphere2.f[35:59]"] } cmds.select(mydic["aiStandard"]) 的对象。但另外,您可以使用索引为0的项目数组。

答案 3 :(得分:1)

我接受了约翰的回答并加以改进(我希望如此):

const mutableProxyFactory = (mutableTarget, mutableHandler = Reflect) => ({
  setTarget(target) {
    new Proxy(target, {});  // test target validity
    mutableTarget = target;
  },
  setHandler(handler) {
    new Proxy({}, handler);  // test handler validity
    Object.keys(handler).forEach(key => {
      const value = handler[key];
      if (Reflect[key] && typeof value !== 'function') {
        throw new Error(`Trap "${key}: ${value}" is not a function`);
      }
    });
    mutableHandler = handler;
  },
  getTarget() {
    return mutableTarget;
  },
  getHandler() {
    return mutableHandler;
  },
  proxy: new Proxy(
    mutableTarget,
    new Proxy({}, {
      // Dynamically forward all the traps to the associated methods on the mutable handler
      get(target, property) {
        return (_target, ...args) => mutableHandler[property].apply(mutableHandler, [mutableTarget, ...args]);
      }
    }),
  )
});

一些重要的区别:

  1. John的版本只有一个mutableTarget/Handler的所有结果共享一个mutableProxyFactory变量,因此您可能无法多次调用它。
  2. 允许处理程序具有其他键,因为没有理由不应该这样做。
  3. 处理程序中的陷阱已将this绑定到处理程序,就像普通的Proxy一样。
  4. 用户可以为处理程序和目标提供初始值。实际上需要目标值。
  5. mutableProxyFactory未全局分配给window
相关问题