Adam
Adam

Reputation: 5233

How to get the target of a JavaScript Proxy?

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

Upvotes: 88

Views: 88444

Answers (13)

jollege
jollege

Reputation: 1147

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.

Upvotes: -1

Eduardo Rosostolato
Eduardo Rosostolato

Reputation: 858

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

Upvotes: 3

Youth overturn
Youth overturn

Reputation: 417

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

Upvotes: -1

Caleb Waldner
Caleb Waldner

Reputation: 1139

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

Upvotes: 85

wei zhang
wei zhang

Reputation: 11

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

Upvotes: -1

Davidiusdadi
Davidiusdadi

Reputation: 521

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!

Upvotes: 14

sgrubsmyon
sgrubsmyon

Reputation: 1229

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!

Upvotes: 28

timkay
timkay

Reputation: 727

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

Upvotes: 4

Yuci
Yuci

Reputation: 30109

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

Upvotes: 3

Soumik Chatterjee
Soumik Chatterjee

Reputation: 89

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.

Upvotes: 8

ragnar
ragnar

Reputation: 1302

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

Upvotes: 2

Atnaize
Atnaize

Reputation: 1816

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

Upvotes: -2

Rashad Saleh
Rashad Saleh

Reputation: 2847

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.

Upvotes: 53

Related Questions