【问题标题】:How to get the target of a JavaScript Proxy?如何获取 JavaScript 代理的目标?
【发布时间】:2018-12-08 08:13:18
【问题描述】:
function createProxy() {
    const myArray = [Math.random(), Math.random()];
    return new Proxy(myArray, {});
}

const myProxy = createProxy();

这里如何访问myProxytarget(即myArray)?

我尝试了很多方法。在谷歌上搜索了很多博文,但没有找到目标:(

【问题讨论】:

  • 你不能。这就是为什么它是一个代理。你想做什么,为什么需要这个?
  • @Bergi 因为如果我有一个带有循环引用的对象,并且这些循环引用在代理中,则无法安全地字符串化我的对象。我会得到超出堆栈大小的错误。 :(
  • 你能举个例子吗?循环引用(我认为您的意思是那些,而不是依赖项)应该可以与代理一起正常工作,因为myProxy === myProxy 仍然有效。没有什么需要抓住目标。

标签: javascript ecmascript-6 proxy


【解决方案1】:

使用私有Symbol 来捕获属性。然后给出一个公共函数来测试对象是否为这个Symbol返回true

var sym_Test = Symbol('test')
let proxyHandler = {        
    get: function(pValues:any, prop:any, receiver:any){
        if(prop == sym_Test)
            return true;
        
        return getValue(pValues, prop)
    },    
}

export function IsMyProxy(some:any){
    return some[sym_Test]
}

【讨论】:

  • OP 要求的是目标,而不是 true
  • 符号不是私有的。
【解决方案2】:

就像已经说过的其他答案一样,代理获取陷阱可能是一个优雅的解决方案。

const IDENTITY = Symbol('proxy_target_identity')
const handler = {
  get: (target, property, receiver) => {
    if (property === IDENTITY) {
      return target
    }
    return Reflect.get(target, property, receiver)
  }
}
function createProxy() {
    const myArray = [Math.random(), Math.random()];
    return new Proxy(myArray, handler);
}
const myProxy = createProxy();
const orignal_target = myProxy[IDENTITY]

这个代码示例应该非常健壮:

  • 使用Symbol 避免属性名称冲突
  • 使用Reflect.get 代替target[property] 涵盖所有get-scenario
  • 警告:注意原型继承 - 如果您的代理最终被用作原型,not_itself_a_proxy[IDENTITY] 调用将不会返回 not_itself_a_proxy,而是原型的“身份”!

【讨论】:

  • 为了避免原型问题,就做&& target === receiver
【解决方案3】:

我发现(使用 Vue.js,有时会涉及代理对象,例如在观看组件道具时)我可以使用 JSON.stringify 获取目标和数组两种情况:

let myTarget = JSON.parse(JSON.stringify(myProxy))

这种方法也适用于 Array 目标,而 Object.assign({}, myProxy) 仅适用于目标是 Object。

但我对 JavaScript 代理非常陌生,我的知识有限。我可能不理解这种方法的局限性和注意事项。不过,也许它对某人有帮助!

【讨论】:

  • 谢谢。这就是我需要的。我需要 Vue 创建的代理的目标,所以我可以将它作为获取请求的一部分传递。
【解决方案4】:

其他答案给出了一些很好的解决方案。这是@Yuci 为类提炼出来的答案,在这种情况下,它就像定义某个特殊名称的实例变量一样简单。 Proxy get 函数返回它,底层目标也是如此。

class Foo {
    constructor() {
        this.__target__ = this;
        return new Proxy(this, {
            get: function (target, name) {
                if (name in target) return target[name];
                // your code here
            }
        });
    }
}

let foo = new Foo();
let target = foo.__target__;
console.log('proxied Foo', foo);
console.log('recovered target', target, target.__target__.__target__);

【讨论】:

    【解决方案5】:

    如何添加以下获取陷阱:

    const handler = {
      get: (target, property, receiver) => {
        if (property === 'myTarget') {
          return target
        }
        return target[property]
      }
    }
    
    const myArray = [Math.random(), Math.random()];
    
    function createProxy() {
    //     const myArray = [Math.random(), Math.random()];
        return new Proxy(myArray, handler);
    }
    
    const myProxy = createProxy();
    

    并且可以通过myProxy.myTarget获取代理的目标:

    console.log(myProxy.myTarget) // [0.22089416118932403, 0.08429264462405173]
    console.log(myArray === myProxy.myTarget) // true
    

    【讨论】:

      【解决方案6】:

      有一个聪明的方法可以做到这一点 - 您可以向代理添加 get 陷阱并让它返回 目标 有条件的。就这样..

      let resolveMode = false;  // Switch that controls if getter returns target or prop. 
      
      function resolve(obj) {
          resolveMode = true;  // Turn on our switch
          let target = obj.anything;  // This gets the target not the prop!
          resolveMode = false;  // Turn off the switch for the getter to behave normally
          return target;  // Return what we got!
      }
      
      function createProxy() {
          const myArray = [Math.random(), Math.random()];
          return new Proxy(myArray, {
              get: function(target, prop) {
                  if (resolveMode) return target;  // This is where the magic happens!
                  else return target[prop];        // This is normal behavior..
              }
          });
      }
      
      const myProxy = createProxy();
      let target = resolve(myProxy);
      

      请记住,添加到陷阱的代码行越多,对象的性能就越慢。希望这会有所帮助。

      【讨论】:

      • 只有当您可以访问代理的源代码时,这才是一个选项。
      【解决方案7】:

      如果目标是对象,则可以。

      您必须在目标中创建一个函数来检索它,仅此而已。

      例子:

      class AnyClass {
         constructor() {
            this.target = this;
      
            return new Proxy(this, this);
         }
      
         get(obj, prop) {
            if (prop in obj)
                return this[prop];
      
            // your stuff here
         }
      
         getTarget() {
            return this.target;
         }
      }
      

      然后当你打电话时:

      let sample = new AnyClass;
      console.log(sample.getTarget());
      

      会如你所愿返回目标:)

      【讨论】:

      • 不要使用目标作为处理程序。那里有疯狂。
      【解决方案8】:

      由于代理包含一个对象你也可以这样做

      Object.keys( my_proxy )
      

      然后很容易检索诸如Object.keys( my_proxy ).length之类的东西

      【讨论】:

        【解决方案9】:

        您可以使用Object.assign()复制代理返回的数据:

        const target_copy = Object.assign({}, my_proxy);
        

        这适用于代理/目标上现有的所有enumerable own properties

        【讨论】:

        • 我的示例场景需要处理作为来自 Web API 调用的承诺拒绝返回的代理。整行如下所示:var m = Object.assign({}, rejected)[0].message;。谢谢拉沙德!
        • 这不能回答问题。 target_copy的数据仍然会受到代理访问者的影响;并且您将没有对原始对象的引用。
        • 如果目标是一个数组并且你想取回数组,这不起作用。
        猜你喜欢
        • 1970-01-01
        • 2023-01-26
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 2011-10-12
        • 2021-04-19
        • 2012-03-16
        • 2022-08-17
        相关资源
        最近更新 更多