如何测试对象是否是代理?

时间:2016-04-02 11:25:50

标签: javascript ecmascript-6 proxy-classes

我想测试JavaScript对象是否为Proxy。琐碎的方法

if (obj instanceof Proxy) ...

在这里不起作用,也没有遍历Proxy.prototype的原型链,因为所有相关操作都得到了基础目标的有效支持。

是否可以测试任意对象是否是代理?

12 个答案:

答案 0 :(得分:19)

在我当前的项目中,我还需要一种方法来定义某些东西是否已经是代理,主要是因为我不想在代理上启动代理。为此我只是在我的处理程序中添加了一个getter,如果请求的变量为“__Proxy”,它将返回true:

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

可能不是最好的解决方案,但我认为这是一个优雅的解决方案,在序列化时也不会弹出。

答案 1 :(得分:12)

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

  

无法确定对象是否是代理(透明虚拟化)。

答案 2 :(得分:8)

在Node.js 10中,您可以使用util.types.isProxy

例如:

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

答案 3 :(得分:6)

使用try.catch的window.postMessage()

postMessage无法序列化与structured clone algorithm不兼容的对象,例如Proxy。

function isProxy(obj) {
    try {
        postMessage(obj, "*");
    } catch (error) {
        return error && error.code === 25; // DATA_CLONE_ERR
    }

    return false;
}

答案 4 :(得分:5)

创建一个新符号:

let isProxy = Symbol("isProxy")

在代理处理程序的get方法内,您可以检查key是否是您的符号,然后检查return true

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

    // normal get handler code here
}

然后,您可以使用以下代码检查对象是否是代理之一:

if (myObject[isProxy]) ...

答案 5 :(得分:5)

instanceof Proxy添加“支持”:

我不推荐这样做,但是如果您想添加对instanceof的支持,则可以在实例化任何代理之前执行以下操作:

(() => {
  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

答案 6 :(得分:3)

我发现的最好方法是创建一组弱代理对象。在构建和检查代理对象时,可以递归执行此操作。

    var myProxySet = new WeakSet();
    var myObj = new Proxy({},myValidator);
    myProxySet.add(myObj);

    if(myProxySet.has(myObj)) {
        // Working with a proxy object.
    }

答案 7 :(得分:3)

实际上,有一种解决方法可以确定对象是否是代理,这是基于几个假设。首先,当页面可以启动不安全的扩展时,可以通过C ++扩展或浏览器中的特权网页轻松地解决node.js环境中的代理确定。其次,Proxy是相对较新的功能,因此它在旧浏览器中不存在 - 因此解决方案仅适用于现代浏览器。

JS引擎无法克隆函数(因为它们绑定了激活上下文和其他一些原因),但是根据定义,Proxy对象由包装器处理程序组成。因此,要确定对象是否是代理,它足以启动强制对象克隆。可以通过postMessage功能完成。

如果object是Proxy,即使它不包含任何函数,也无法复制。例如,Edge和Chrome在尝试发布代理对象时会产生以下错误:[object DOMException]: {code: 25, message: "DataCloneError", name: "DataCloneError"}Failed to execute 'postMessage' on 'Window': [object Object] could not be cloned.

答案 8 :(得分:2)

似乎没有标准方法,但对于Firefox特权代码,您可以使用

Components.utils.isProxy(object);

例如:

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

答案 9 :(得分:1)

Matthew Brichacek和David Callanan对于您自己创建的Proxy给出了很好的答案,但是如果不是这种情况,那么可以添加一些

想象一下,您有一个外部函数创建了无法修改的代理

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

在执行任何外部代码之前,我们可以像Matthew Brichacek一样重新定义代理构造函数并使用WeakSet来存储代理。 我不使用类,因为否则代理将具有原型,并且可以检测到代理已更改。

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

相同的方法,但带有像David Callanan这样的Symbol

  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

我认为第一种更好,因为您只更改构造函数,而第二种创建代理的代理,而问题的目的是避免这种情况。

如果代理是在iframe中创建的,则不起作用,因为我们仅为当前帧重新定义了代理。

答案 10 :(得分:0)

我相信我已经找到一种更安全的方式来检查该物品是否是代理。这个答案的灵感来自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
    });
}

基本上,我所做的是,不是将__isProxy字段添加到目标,而是在代理的getter中添加了此检查:if (property === Symbol.for("__isProxy")) return true;。这样,如果您使用for-in循环或Object.keysObject.hasOwnProperty,则__isProxy将不存在。

不幸的是,即使您可以设置__isProxy的值,由于对getter的检查,您将永远无法检索它。因此,在设置字段时应该抛出错误。

如果您认为可能要将Symbol用作其他属性,则还可以使用__isProxy来检查变量是否是Proxy。

最后,我还为代理服务器的目标添加了类似的功能,这同样很难检索。

答案 11 :(得分:-1)

有两种方法来代理对象。一个是new Proxy,另一个是Proxy.revocable。我们可以窥探他们,以便将代理对象记录到秘密列表中。然后我们通过检查是否确定对象是代理对象 它存在于秘密列表中。

要窥探函数,我们可能会编写包装器或使用内置代理。后者意味着使用代理来代理new Proxy以及Proxy.recovable,这里是fiddle来演示这个想法。

要投放old Proxy API类似nodejs-v5.8.0代理,我们可以使用Proxy.createFunction代理Proxy.createProxy.createFunction来应用相同的想法。