How to test if an object is a Proxy?

JavascriptEcmascript 6Proxy Classes

Javascript Problem Overview


I would like to test if a JavaScript object is a Proxy. The trivial approach

if (obj instanceof Proxy) ...

doesn't work here, nor does traversing the prototype chain for Proxy.prototype, since all relevant operations are effectively backed by the underlying target.

Is it possible to test if an arbitrary object is a Proxy?

Javascript Solutions


Solution 1 - Javascript

In my current project I also needed a way of defining if something was already a Proxy, mainly because I didn't want to start a proxy on a proxy. For this I simply added a getter to my handler, which would return true if the requested variable was "__Proxy":

function _observe(obj) {
  if (obj.__isProxy === undefined) {
    var ret = new Proxy(obj || {}, {
      set: (target, key, value) => {
        /// act on the change
        return true;
      },
      get: (target, key) => {
        if (key !== "__isProxy") {
          return target[key];
        }

        return true;
      }
    });
    return ret;
  }

  return obj;
}

Might not be the best solution, but I think it's an elegant solution, which also doesn't pop up when serializing.

Solution 2 - Javascript

In Node.js 10 you can use util.types.isProxy.

For example:

const target = {};
const proxy = new Proxy(target, {});
util.types.isProxy(target);  // Returns false
util.types.isProxy(proxy);  // Returns true

Solution 3 - Javascript

Create a new symbol:

let isProxy = Symbol("isProxy")

Inside the get method of your proxy handler you can check if the key is your symbol and then return true:

get(target, key)
{
    if (key === isProxy)
        return true;

    // normal get handler code here
}

You can then check if an object is one of your proxies by using the following code:

if (myObject[isProxy]) ...

Solution 4 - Javascript

From http://www.2ality.com/2014/12/es6-proxies.html:

> It is impossible to determine whether an object is a proxy or not (transparent virtualization).

Solution 5 - Javascript

Adding 'support' for instanceof Proxy:

I don't recommend it, but If you want to add support for instanceof, you could do the following before instantiating any Proxies:

(() => {
  var proxyInstances = new WeakSet()
  
  // Optionally save the original in global scope:
  originalProxy = Proxy

  Proxy = new Proxy(Proxy, {
    construct(target, args) {
      var newProxy = new originalProxy(...args)
      proxyInstances.add(newProxy)
      return newProxy
    },
    get(obj, prop) {
      if (prop == Symbol.hasInstance) {
        return (instance) => {
          return proxyInstances.has(instance)
        }
      }
      return Reflect.get(...arguments)
    }
  })
})()

// Demo:

var a = new Proxy({}, {})
console.log(a instanceof Proxy) // true
delete a

var a = new originalProxy({}, {})
console.log(a instanceof Proxy) // false
delete a

Solution 6 - Javascript

In fact, there is workaround for determine if object is proxy, which is based on several assumptions. Firstly, Proxy determination can be easily solved for node.js environment via C++ extensions or privileged web-page in browser, when page can launch unsecure extensions. Secondly, Proxy is relative new functionality, so it does not exist in old browsers - so solution works only in modern browsers.

JS engine can't clone functions (Since they have bindings to activation context and some other reasons), but Proxy object by definition consists of wrapper handlers. So to determine if object is proxy, it's enough to initiate force object cloning. In can be done via postMessage function.

If object is Proxy, it will failed to copy even it does not contain any functions. For example, Edge and Chrome produces following errors while try to post Proxy object: [object DOMException]: {code: 25, message: "DataCloneError", name: "DataCloneError"} and Failed to execute 'postMessage' on 'Window': [object Object] could not be cloned..

Solution 7 - Javascript

Use window.postMessage() with try-catch to get a hint

postMessage cannot serialize objects which incompatible with structured clone algorithm, like Proxy.

function shouldBeCloneable(o) {
	const type = typeof o;
	return (
        o?.constructor === ({}).constructor ||
		type === "undefined" ||
		o === null ||
		type === "boolean" ||
		type === "number" ||
		type === "string" ||
		o instanceof Date ||
		o instanceof RegExp ||
		o instanceof Blob ||
		o instanceof File ||
		o instanceof FileList ||
		o instanceof ArrayBuffer ||
		o instanceof ImageData ||
		o instanceof ImageBitmap ||
		o instanceof Array ||
		o instanceof Map ||
		o instanceof Set
	);
}

function isCloneable(obj) {
	try {
		postMessage(obj, "*");
	} catch (error) {
		if (error?.code === 25) return false; // DATA_CLONE_ERR
	}

	return true;
}

function isProxy(obj){
    const _shouldBeCloneable = shouldBeCloneable(obj);
    const _isCloneable = isCloneable(obj);

    if(_isCloneable) return false;
    if(!_shouldBeCloneable) return "maybe";
    
    return _shouldBeCloneable && !_isCloneable;
}

console.log("proxied {}", isProxy(new Proxy({},{})));
console.log("{}", isProxy({}));

console.log("proxied []", isProxy(new Proxy([],{})));
console.log("[]", isProxy([]));

console.log("proxied function", isProxy(new Proxy(()=>{},{})));
console.log("function", isProxy(()=>{}));

console.log("proxied Map", isProxy(new Proxy(new Map(),{})));
console.log("new Map()", isProxy(new Map()));

class A{};
console.log("proxied class", isProxy(new Proxy(A,{})));
console.log("class", isProxy(A));

console.log("proxied class instance", isProxy(new Proxy(new A(),{})));
console.log("class instance", isProxy(new A()));

Solution 8 - Javascript

The best method I have found is creating a weak set of the proxy objects. You can do this recursively when you are building and checking your proxied objects.

    var myProxySet = new WeakSet();
    var myObj = new Proxy({},myValidator);
    myProxySet.add(myObj);
    
    if(myProxySet.has(myObj)) {
        // Working with a proxy object.
    }

Solution 9 - Javascript

It seems there is no standard way, but for Firefox privileged code you can use

Components.utils.isProxy(object);

For example:

Components.utils.isProxy([]); // false
Components.utils.isProxy(new Proxy([], {})); // true

Solution 10 - Javascript

Matthew Brichacek and David Callanan give good answers for Proxy you create yourself but if it is not the case here are some additions

Imagine you have an external function creating Proxy that you can't modify

const external_script = ()=>{
    return new Proxy({a:5},{})
}

Before any externals code executions, we can redefine the proxy constructor and use a WeakSet to store proxy as Matthew Brichacek does. I don't use a class because otherwise Proxy will have a prototype and it will be detectable that Proxy has been changed.

const proxy_set = new WeakSet()
window.Proxy = new Proxy(Proxy,{
      construct(target, args) {
        const proxy = new target(...args)
        proxy_set.add(proxy)
        return proxy
      }
})
const a = external_script()
console.log(proxy_set.has(a)) //true

Same method but with Symbol like David Callanan

  const is_proxy = Symbol('is_proxy')
  const old_Proxy = Proxy
  const handler = {
    has (target, key) {
      return (is_proxy === key) || (key in target)
    }
  }
  window.Proxy = new Proxy(Proxy,{
      construct(target, args) {
          return new old_Proxy(new target(...args), handler)
      }
  })
  const a = external_script()
  console.log(is_proxy in a) //true

I think the first is better because you only change the constructor while the second creates a proxy of a proxy while the purpose of the question was to avoid this.

It does not work if the proxy is created inside an iframe because we only have redefined the proxy for the current frame.

Solution 11 - Javascript

It is impossible to detect if something is a Proxy according to the JS language specification.

node does provide a mechanism via native code, but I don't recommend its use - you're not supposed to know if something is a Proxy.

Other answers that suggest wrapping or shadowing the global Proxy will not actually work cross-realm (ie, iframes, web workers, node's vm module, wasm, etc).

Solution 12 - Javascript

There are two ways to proxy an object. One is new Proxy, another is Proxy.revocable. We may spy them so that proxied object are recorded to a secret list. Then we determine an object is a proxied object by checking if it exists in the secret list.

To spy functions, we may write wrappers or use the built-in Proxy. The latter means that use Proxy to proxy new Proxy as well as Proxy.recovable, here is a fiddle to demo the idea.

To serve the old Proxy API like nodejs-v5.8.0 Proxy, we may apply the same idea by using Proxy.createFunction to proxy Proxy.create and Proxy.createFunction.

Solution 13 - Javascript

I believe I have found a safer way to check if the item is a proxy. This answer was inspired by Xabre's answer.

function getProxy(target, property) {
    if (property === Symbol.for("__isProxy")) return true;
    if (property === Symbol.for("__target")) return target;
    return target[property];
}

function setProxy(target, property, value) {
	if (property === Symbol.for("__isProxy")) throw new Error("You cannot set the value of '__isProxy'");
	if (property === Symbol.for("__target")) throw new Error("You cannot set the value of '__target'");
	if (target[property !== value]) target[property] = value;
	return true;
}

function isProxy(proxy) {
    return proxy == null ? false : !!proxy[Symbol.for("__isProxy")];
}

function getTarget(proxy) {
	return isProxy(proxy) ? proxy[Symbol.for("__target")] : proxy;
}

function updateProxy(values, property) {
    values[property] = new Proxy(getTarget(values[property]), {
        set: setProxy,
        get: getProxy
    });
}

Essentially what I've done is, instead of adding the __isProxy field to the target, I added this check: if (property === Symbol.for("__isProxy")) return true; in the getter of the proxy. This way if you are using a for-in loop or Object.keys or Object.hasOwnProperty, __isProxy will not exist.

Unfortunately, even though you can set the value of __isProxy, you will never be able to retrieve it, due the check on the getter. Therefore you should throw an error when the field gets set.

You could also use a Symbol to check whether a variable is a Proxy, if you think that its likely you want to use __isProxy as a different property.

Finally, I also added similar functionality for the target of the proxy, which can also be quite as hard to retrieve.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGOTO 0View Question on Stackoverflow
Solution 1 - JavascriptXabreView Answer on Stackoverflow
Solution 2 - JavascriptAllyView Answer on Stackoverflow
Solution 3 - JavascriptDavid CallananView Answer on Stackoverflow
Solution 4 - Javascriptuser663031View Answer on Stackoverflow
Solution 5 - JavascriptADJenksView Answer on Stackoverflow
Solution 6 - JavascriptVladislav IhostView Answer on Stackoverflow
Solution 7 - JavascriptEylon SultanView Answer on Stackoverflow
Solution 8 - JavascriptMatthew BrichacekView Answer on Stackoverflow
Solution 9 - JavascriptOriolView Answer on Stackoverflow
Solution 10 - JavascriptbormatView Answer on Stackoverflow
Solution 11 - JavascriptLJHarbView Answer on Stackoverflow
Solution 12 - Javascriptuser943702View Answer on Stackoverflow
Solution 13 - Javascriptnick zoumView Answer on Stackoverflow