104

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?

7
  • 1
    Maybe proxy the Proxy? I mean proxy the Proxy function before any script involving Proxy.. Commented Apr 2, 2016 at 16:24
  • 2
    Isn't the very purpose of proxies that you cannot distinguish them from a "normal" object? Why would you want to test this? Commented Apr 2, 2016 at 16:42
  • 5
    @Bergi well, certainly that's not the main purpose of proxies. For the rest, I'm a bit surprised you cannot figure out a use case for this test. Commented Apr 2, 2016 at 16:49
  • 1
    I wrote a fiddle to clarify the idea,... jsfiddle.net/ycw7788/uauoxn7o Commented Apr 2, 2016 at 17:06
  • 1
    @JoshuaCheek For debugging, look at the object with a debugger. It will be capable of telling you whether it's a proxy or not. Commented Jul 24, 2018 at 8:15

15 Answers 15

42

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
Sign up to request clarification or add additional context in comments.

6 Comments

Question does not specify node. However, if you're using it, this is the best way.
This is the only correct positive answer free from loopholes. If you don’t mind the non-portability, this is the way to do it. That said, it’s probably still best not to treat proxies differently from other objects.
Correction: @Oriol’s answer is also loophole-free and non-portable (not sure how up-to-date it is, though).
How does this magic works?
@TimeKiller The interpreter just examines the internal object shape directly.
|
40

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.

5 Comments

Thanks! I'm doing some sort of deep proxy, and I was getting into this problem as well.
Great, I'm using this approach but with a small twist: const IS_PROXY = Symbol("is-proxy"); ... if (!obj[IS_PROXY])
@MatthewJoelRodríguezLlanos Just ensure that the symbol is stored in some closure, because Symbol("is-proxy") !== Symbol("is-proxy"), or that you use Symbol.for
This only works if you are in control of creating the proxy trap.
This won’t even work on ({"__isProxy":false}).
35

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]) ...

5 Comments

Interesting! Just a note for others, this will only work if you control the Proxy get trap. Otherwise this won't work if you don't own the object.
@snewcomer Thanks. That is right and I think this satisfies the common use-case. (I personally don't believe you should be checking if foreign objects are proxies because then you are writing implementation-specific code and overriding the whole abstract nature/purpose of proxies in the first place).
I worked upon your idea to include the cases where the Proxy (and its get trap) is in library code (and out of the client's code reach) and added on-site testable example here
What’s stopping the user from writing ({ [isProxy]: true })?
@user3840170 I suppose the idea is not to expose the isProxy symbol anywhere outside the piece of code that constructs the instance.
23

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

3 Comments

I wouldn't link an article that starts with a note "this blog post is outdated" :-) However, exploringjs.com/es6/ch_proxies.html states exactly the same
@BrassApparatus Actually, it turns out that both his comment and my answer are wrong, as detailed here.
@user663031 the answer is not wrong. Proxies might not be transparent to builtin methods, but they are to ordinary JavaScript, and it is not possible to determine from outside whether an object is a proxy object.
21

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

Comments

14

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

10 Comments

It works! With this solution for browsers, and this other solution for NodeJS, we have the two main contexts covered. :)
This only tells you if the object is not cloneable, right? So this non-proxy, returns true from your function: isProxy({a:()=>{}}). It should just be called "isNotCloneable".
This answer is not correct. Structured clone algorithm also throws DataCloneError for Function objects and DOM nodes.
Is there a reason shouldBeCloneable({}) is false? It seems to me that you should check for o?.constructor === ({}).constructor...
An easier way to check whether an object is cloneable is to try calling structuredClone(obj) and catch the exception.
|
11

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..

6 Comments

Interesting. I wonder if there's a way to make this method work also with objects that contain functions, DOM elements, and other unclonable stuff.
+1 for finding one true solution for many cases. Turns out, in the same way, proxied Event objects cannot be dispatched (i.e., dispatchEvent) and proxied DOM elements cannot be attached to the DOM. It's possible there are other unique ones too (like audio context nodes).
@Codesmith I found myself here after wondering why proxied events wouldn't dispatch.
"So to determine if object is proxy, it's enough to initiate force object cloning." Interesting. I thought by definition proxy was supposed to abstract away functions into what appears to just be property-value pairs. So I would've thought a force clone would've been equivalent to iterating each property in the proxy and cloning its value at clone-time. (Because that's what it's supposed to appear like to the javascript user)
@DavidCallanan It sounds like a "leaky abstraction"
|
8

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

2 Comments

I don't understand this. myProxySet.has() will succeed if the object is a Proxy or not.
@MatthewDean: I think he means that you're responsible for ALWAYS placing proxies you create into the proxy set, not that the set has some ability to discern if the values it contains are proxies. I.e. if you are careful and keep track of all the proxies you generate you can check if an object is one that you proxied (but it won't help you with objects that some third party has proxied)
6
+200

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 (i.e. between iframes, Web Workers, node's vm module, WebAssembly, etc.).

10 Comments

Sure, although it's trivially googleable: tc39.es/ecma262, but i'm a delegate on the JS language committee, and not being able to detect a Proxy is a critical motivation in its design, so I'm pretty sure it's not a fantasy, mine or otherwise.
It's not said explicitly anywhere in the spec - it's an emergent property of the design. You'll just have to accept that. Your answer doesn't at all solve this - certainly if the creator of the Proxy wishes to reveal that information it can do so, but the ask is to detect a Proxy you did not create, which is impossible by design (implicit is, without the cooperation of the Proxy creator).
@JanTuroň It is not said anywhere in the ECMAScript specification that you cannot solve the halting problem. Therefore it is possible, and any claim to the contrary is just your fantasy.
@JanTuroň creating a proxy to another object can't tell you if that object is itself a proxy, that's the point (unless it intentionally leaks that information, of course). It isn't written in the spec because the spec doesn't contain design motivations, it contains normative requirements. I was an editor of the spec for 3 years, so if you won't take my word for it, you're welcome to continue believing whatever nonsense you like.
Unfortunately this also isn't a solution; it fails with eg is_proxy_simple(new Proxy(Object.freeze({}), {})), because it relies on being able to mutate the [[Prototype]] of the object.
|
5

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

Comments

3

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.

2 Comments

this is the most practical solution IMO.
({ [is_proxy]: true })
2

The has Proxy trap

Working on David Callanan's answer, I find the has Proxy trap more appropriate for this: extra testing each time proxy property is accessed causes immeasurable efficiency loss, which can add up if frequently called or having further extra logic in the get trap.

const DetectableProxy = obj => new Proxy(obj, {
  has(o, prop) {
    if(prop == Symbol.for("proxy")) return true;
    return prop in o;
  }
});

function isDetectableProxy(obj) {
  if(obj === null || typeof obj != "object") return false;
  return Symbol.for("proxy") in obj;
}

// -- EXAMPLE --

var obj = { foo: "bar" };
var sayObj = new Proxy(obj, { get: (o, prop) => "I say " + o[prop] });
var exposedSayObj = DetectableProxy(sayObj);

console.log(exposedSayObj.foo); // I say bar
console.log(isDetectableProxy(exposedSayObj)); // true
console.log(isDetectableProxy(sayObj)); // false
for(const prop in exposedSayObj) console.log(prop); // foo

4 Comments

That's some smart thinking
({ [Symbol.for('proxy')]: null }) defeats this.
@dumbass That's not what you are supposed to do with symbols: Symbols are often used to add unique property keys to an object that won't collide with keys any other code might add to the object, and which are hidden from any mechanisms other code will typically use to access the object, see here
An adversary wanting to break your code is not going to care what they are “supposed to” do.
-1

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.

5 Comments

This approach won't work for proxies created in other realms (like iframes, or node's vm modules), because you can't spy on globals (like Proxy) in other realms.
@LJHarb Are you sure? I actually tried that after reading your comment but had no problem in replacing the Proxy object across iframe realms. I didn't test that with vm modules but don't see why it shouldn't work the same way.
@GOTO0 what i mean is, unless you can run code first in that other realm, you can't guarantee it has a shadowed Proxy. For iframes specifically, I could just create a brand new one and get access to the original.
@LJHarb If so, it's a valid point and it equally applies to most other answers I see here. It's unfortunate that you chose the least visible answer to post your comment.
I posted an answer; feel free to vote it up if you find it valuable.
-1

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.

Comments

-1

The shortest form based on the answer of ADJenks:

var GlobalProxy = Proxy
Proxy = function Proxy(a,b) {
    if ((typeof this != "object") || !(this instanceof Proxy)) {
        return new Proxy(a,b)
    }
    var getLastPrototype = function(obj,parent){
        var proto = Object.getPrototypeOf(obj)
        if (proto !== null) {
            return getLastPrototype(proto,obj)
        }
        return parent?parent:obj
    }
    Object.setPrototypeOf(getLastPrototype(a),this)
    return new GlobalProxy(a,b)
}

With that it is possible to check if an Object is proxied using instanceof Proxy.

Here are some test cases:

class DevAbstr {
    devTest() {
        console.log('runned devTest without problems')
        return "SUCCESS"
    }
}
class DevObj extends DevAbstr {}
var test = Proxy(new DevObj,{
    set: function (t, k, v) {
        if (k === "IS_PROXY") throw "IS_PROXY is reserved"
        if (typeof t.set == "function") {
            t.set(k,v)
        } else {
            t[k] = v;
            console.log("original",t, k, v)
        }
        return true
    },
    get: function (t, k) {
        if (k === "IS_PROXY") return true
        if (k === "PROXIED_OBJECT") return t
        if (typeof t.get == "function") {
            return t.get(k)
        } else {
            return t[k]
        }
        return false
    }
})
console.log("test instanceof Proxy", test instanceof Proxy) // true
console.log("test instanceof DevAbstr", test instanceof DevAbstr) // true
console.log("test instanceof DevObj", test instanceof DevObj) // true
test.blubb = 123
console.log("test.IS_PROXY", test.IS_PROXY) // true
console.log("test",test) // Proxy(Object)
console.log("test.PROXIED_OBJECT",test.PROXIED_OBJECT) // source object
console.log("test.devTest()",test.devTest()) // works

;(function() {
    console.log("Proxy",Proxy)
})()

 // valid!
for (var k in test) {
    console.log(k+': ',test[k])
}

I also compiled this to ES5 without problems.

This approach is ugly, i knew, but it works quite well...

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.