How to get the target of a JavaScript Proxy?

2019-07-15 12:40发布

问题:

function createProxy() {
    const myArray = [Math.random(), Math.random()];
    return new Proxy(myArray, {});
}

const myProxy = createProxy();

How to access the target (which is myArray) of myProxy here?

I've tried many ways. Was googled many blog posts, but found no way to get the target :(

回答1:

There is a clever way to do this - You can add a get trap to the proxy and have it return the target conditionally. Like so..

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);

Remember that the more lines of code you add to traps, the slower the object's performance gets. Hope this helps.



回答2:

The other answers gave some good solutions. Here is @Yuci's answer distilled down for classes, in which case, it's as simple as defining an instance variable of some special name. The Proxy get function returns it, and so does the underlying target.

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__);


回答3:

You can make a copy of the data returned by the proxy using Object.assign():

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

This will work for all enumerable own properties existing on the proxy/target.



回答4:

You can if the target is an object.

You will have to create a function in target to retrieve it, that's all.

Example:

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;
   }
}

And then when you call:

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

Will return you the target as you expect :)



回答5:

How about adding the following get trap:

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

And you can get the target of the proxy by myProxy.myTarget:

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


回答6:

As the proxy contain an object you can also do

Object.keys( my_proxy )

And then it become easy to retrieve thing such as Object.keys( my_proxy ).length



回答7:

Like the other answers already said a proxy get trap can be an elegant solution.

const IDENTITY = Symbol('proxy_target_identity')
const handler = {
  get: (target, property, receiver) => {
    if (property === IDENTITY && target.hasOwnProperty(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]

This code sample should be quite robust as it:

  • avoids property name conflicts by using a Symbol
  • covers all get-scenarios by using Reflect.get instead of target[property]
  • works with prototype-inheritance - in case your proxy ends up being used as a prototype your inheritee will not return itself despite never being actually proxied itself.