我想测试JavaScript对象是否为Proxy。琐碎的方法
if (obj instanceof Proxy) ...
在这里不起作用,也没有遍历Proxy.prototype
的原型链,因为所有相关操作都得到了基础目标的有效支持。
是否可以测试任意对象是否是代理?
答案 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.keys
或Object.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.create
和Proxy.createFunction
来应用相同的想法。