93

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

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

const myProxy = createProxy();

I'd like to have a getProxyTarget function that would return the original object without modifying any other part of the code:

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

const myProxy = createProxy();

function getProxyTarget(proxy){ /* ... your code */ }

console.log(getProxyTarget(myProxy) === original) // should be true
3
  • 4
    You can't. That's why it's a proxy. What are you trying to do, why do you need this? Commented Jun 29, 2018 at 7:54
  • @Bergi Because if I have an object with circular references, and these circular references are in proxies, there is no way to safe stringify my object. I'll get stack size exceeded error. :( Commented Jun 29, 2018 at 7:59
  • 2
    Can you make an example of that, please? Circular references (I think you mean those, not dependencies) should work just fine with proxies, as myProxy === myProxy still holds. Nothing needs to get its hands on the target. Commented Jun 29, 2018 at 8:02

13 Answers 13

92

If you are using Vue 3 and dealing with Proxies, Vue provides some methods to help with this:

import { isProxy, toRaw } from 'vue';

Using these, you can check if an object is a proxy with isProxy, for example:

isProxy(reactiveObjectOrArray) ? 'yup' : 'nope'

And you can extract the raw data using toRaw:

const rawObjectOrArray = toRaw(reactiveObjectOrArray)

More info on toRaw and isProxy

Sign up to request clarification or add additional context in comments.

9 Comments

Bizarrely, isProxy and toRaw only seem to work with reactive objects created with the reactive function, not the more common ref. How on earth are you meant to get the original object for a ref reactive value?
Interesting, I never needed to convert a ref so I never noticed this, but upon testing myself I see that you're correct; a ref doesn't work with those two functions. If someone really needed to convert a ref, they could use toReactive from VueUse, which allowed me to convert my ref into a reactive object. This worked for me, but I believe it only works for objects; string, booleans, and the like might not work using this method.
Did you try that with a template ref, which is what I was trying to figure out the underlying type of? const playfieldRef = ref(null); const pref = playfieldRef; const prefValue = playfieldRef.value; const prefReactive = toReactive(pref); const prIsProxy = isProxy(prefReactive); /* false */ const prRaw = toRaw(prefReactive); /* Proxy {} */
Try const prefReactive = toReactive(pref.value);. Does that work?
Yeah, I tried that one too, it's weird, it gives me back a slightly different Proxy that shows me this in the console when evaluated: Proxy {__v_skip: true, getStage: ƒ, getNode: ƒ}. When I open the object it has [[Handler]]: Object, [[Target]]: Proxy, and [[IsRevoked]]: false on it, but not getStage or getNode functions. Still doesn't seem like an underlying object; unless I'm being dumb and the original library actually exposes a Proxy itself. Hmm.
|
53

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.

3 Comments

My example scenario required handling a proxy returned as a promise reject from a web API call. The full line looked like this: var m = Object.assign({}, rejected)[0].message; . Thanks Rashad!
This doesn't answer the question. The data of target_copy will still be affected by proxy accessors; and you won't have the reference to the original object.
This doesn't work if target is an array and you want to get the array back.
28

I find that (using Vue.js where sometimes Proxy objects are involved, e.g. when watching a component prop) I can obtain the target both if it is an Object and if it is an Array using JSON.stringify:

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

This approach works also with Array targets, whereas Object.assign({}, myProxy) works only if the target is an Object.

But I am very new to JavaScript proxies and my knowledge is limited. I may not understand the limitations and caveats of this approach. Nevertheless, maybe it helps someone!

3 Comments

Thanks. This is what I needed. I needed the target of a proxy created by Vue, so I could pass it as part of a fetch request.
Good answer for older versions, but consider toRaw if on Vue3...
This just creates a copy of the object. A pretty crude one, too.
14

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) {
      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]
  • WARNING: be careful about prototype-inheritance - in case your proxy ends up being used as a prototype the not_itself_a_proxy[IDENTITY] call will not return not_itself_a_proxy but the "identity" of the prototype!

1 Comment

To avoid the prototype problem, just do && target === receiver
8

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.

1 Comment

This is only an option if you have access to the source code of the proxy.
4

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

Comments

4

I found a great way to get the original target by defining the getPrototypeOf handler:

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

const myProxy = createProxy();
const target = Object.getPrototypeOf(myProxy); // will get the original array

Comments

3

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

Comments

2

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

1 Comment

Do not use the target as the handler. There lies madness.
-1

Use a private Symbol to trap a property. Then give a public function to test if the object returns true for this Symbol.

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

2 Comments

OP was asking for the target, not for true.
Symbols are not private.
-1

I found Array.from seems work.

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

const myProxy = createProxy();
const l=Array.from(myProxy)
console.assert(l.every(it=>!isNaN(it)));

1 Comment

That does not access the target of the proxy, that just creates a new array
-1

Lodash cloneDeep works wonders - const targetCopy = _.cloneDeep(myProxy). It roughly does the same as const targetCopy = JSON.stringify(JSON.parse(myProxy)) - but in a neater way. Partly because it doesn't go through the redundant step of converting to a string and back; partly because it has great Typescript support, so TS knows what cloneDeep returns.

2 Comments

That doesn't get the target of the proxy. That creates a new object that looks like the proxy
You're right. As does const targetCopy = JSON.stringify(JSON.parse(myProxy)) which is why cloneDeep is better than that. But yes, it is a copy.
-2

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.