是否可以更改Proxy的目标? [英] Is it possible to change a Proxy's target?

查看:101
本文介绍了是否可以更改Proxy的目标?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  let xhr = new XHRProxy(); 
xhr.open('GET','http:// blah'); //根据URL决定

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

  var foo = {
name(){
returnfoo;
}
};
var bar = {
name(){
returnbar;
}
}
var handler = {
get(target,property,receiver){
if(property ===switchToBar){
// FIXME:这不起作用,因为代理的目标不被暴露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 :(

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

解决方案

定义所有陷阱以委托给所需对象

 (function(){
let mutableTarget;
let mutableHandler;

函数setTarget(target){
if(!(target instanceof Object)){
throw new Error(`Target'$ {target}不是对象`);
}
mutableTarget = target;
}

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

if(typeof value!=='function'){
throw new Error (`Trap'$ {key}:$ {value}不是函数`);
}

if(!Reflect [key]){
抛出新错误(陷阱$ {key}:$ {value}不是有效的陷阱);
}
});
mutableHandler = handler;
}

函数mutableProxyFactory(){
setTarget(()=> {});
setHandler(Reflect);

//将所有陷阱动态转发到可变处理程序上的关联方法
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(`return:$ {proxy()}`);

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

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

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

我觉得有一些原因,这不是开箱即用,但是我没有足够的信息来进一步评论。



在这段时间内,我已经观察了一些事情。代理构造函数调用的原始目标似乎被视为代理身份的一部分。将原始目标设置为普通对象,并将目标交换到某个功能稍后会在调用代理时引发错误。肯定有一些粗糙的边缘,所以谨慎使用。


I have a class that implements the XMLHttpRequest interface. Depending on the URL passed to open(), I can determine whether to use the default XMLHttpRequest or my custom implementation. My idea is to use a proxy to do this:

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

I did some tests using the ES6 Proxy, which seems promising, but unfortunately the proxy target cannot be modified after constructing the Proxy:

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  :(

I think I can accomplish what I want by not setting a target at all - instead defining all traps to delegate to the desired object - but I'm hoping for a simpler solution.

解决方案

Here's a go at "defining all traps to delegate to desired object"

(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()}`);

I feel like there must be some reason this isn't supported out of the box, but I don't have enough information to comment on that further.

After hacking on this for a while, I've observed a few things. It seems the original target that the proxy constructor is called with is treated as part of the proxy's identity regardless. Setting the original target to a plain object and swapping the target to a function later raises an error, when the proxy is called. There are definitely some rough edges to this, so use with caution.

这篇关于是否可以更改Proxy的目标?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆