How to test if an object is a Proxy?

后端 未结 13 2120
予麋鹿
予麋鹿 2020-11-30 07:23

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 tra

相关标签:
13条回答
  • 2020-11-30 07:36

    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.

    0 讨论(0)
  • 2020-11-30 07:42

    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.

    0 讨论(0)
  • 2020-11-30 07:44

    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

    0 讨论(0)
  • 2020-11-30 07:45

    Use window.postMessage() with try-catch

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

    function isProxy(obj) {
        try {
            postMessage(obj, "*");
        } catch (error) {
            return error && error.code === 25; // DATA_CLONE_ERR
        }
    
        return false;
    }
    
    0 讨论(0)
  • 2020-11-30 07:45

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

    0 讨论(0)
  • 2020-11-30 07:45

    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.

    0 讨论(0)
提交回复
热议问题