我想测试JavaScript对象是否是Proxy。琐碎的方法
if (obj instanceof Proxy) ...
在这里不起作用,也没有遍历Proxy.prototype
的原型链,因为所有相关操作都得到了潜在目标的有效支持。
是否可以测试任意对象是否是代理?
在我当前的项目中,我还需要一种方法来定义某些东西是否已经是代理,主要是因为我不想在代理上启动代理。为此我只是在我的处理程序中添加了一个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;
}
可能不是最好的解决方案,但我认为这是一个优雅的解决方案,在序列化时也不会弹出。
我相信我找到了一种更安全的方法来检查该项目是否是代理。这个答案的灵感来自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
字段添加到目标,我添加了这个检查:if (property === Symbol.for("__isProxy")) return true;
在代理的getter中。这样,如果您使用for-in循环或Object.keys
或Object.hasOwnProperty
,__ isProxy将不存在。
不幸的是,即使您可以设置__isProxy
的值,由于检查了getter,您将永远无法检索它。因此,在设置字段时应该抛出错误。
你也可以使用Symbol
来检查一个变量是否是一个代理,如果你认为它可能你想使用__isProxy
作为一个不同的属性。
最后,我还为代理的目标添加了类似的功能,这也很难检索。
代理对象有两种方法。一个是new Proxy
,另一个是Proxy.revocable
。我们可以窥探他们,以便将代理对象记录到秘密列表中。然后我们通过检查对象是否存在于秘密列表中来确定对象是代理对象。
为了窥探函数,我们可以编写包装器或使用内置代理。后者意味着使用代理来代理new Proxy
以及Proxy.recovable
,这里是一个fiddle来演示这个想法。
为了像nodejs-v5.8.0代理服务old Proxy API,我们可以通过使用Proxy.createFunction
代理Proxy.create
和Proxy.createFunction
来应用相同的想法。
来自http://www.2ality.com/2014/12/es6-proxies.html:
无法确定对象是否是代理(透明虚拟化)。
在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
创建一个新符号:
let isProxy = Symbol("isProxy")
在您的代理处理程序的get
方法内,您可以检查key
是否是您的符号,然后return true
:
get(target, key)
{
if (key === isProxy)
return true;
// normal get handler code here
}
然后,您可以使用以下代码检查对象是否是您的代理之一:
if (myObject[isProxy]) ...
我发现的最好方法是创建一组弱代理对象。在构建和检查代理对象时,可以递归执行此操作。
var myProxySet = new WeakSet();
var myObj = new Proxy({},myValidator);
myProxySet.add(myObj);
if(myProxySet.has(myObj)) {
// Working with a proxy object.
}
实际上,有一种解决方法可以确定对象是否是代理,这是基于几个假设。首先,当页面可以启动不安全的扩展时,可以通过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.
。
似乎没有标准方法,但对于Firefox特权代码,您可以使用
Components.utils.isProxy(object);
例如:
Components.utils.isProxy([]); // false
Components.utils.isProxy(new Proxy([], {})); // true
Matthew Brichacek和David Callanan为你自己创建的代理提供了很好的答案但是如果不是这样的话,那么有一些补充
想象一下,您有一个无法修改的外部函数创建代理
const external_script = ()=>{
return new Proxy({a:5},{})
}
在任何外部代码执行之前,我们可以重新定义代理构造函数并使用WeakSet来存储代理,如Matthew Brichacek所做的那样。我不使用类,因为否则Proxy将有一个原型,并且可以检测到Proxy已被更改。
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
同样的方法,但像大卫卡拉南一样的符号
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中创建的,则它不起作用,因为我们只重新定义了当前帧的代理。
根据JS语言规范,无法检测某些东西是否是Proxy
。
node确实通过本机代码提供了一种机制,但我不建议使用它 - 你不应该知道某些东西是否是Proxy
。
其他建议包装或遮蔽全局Proxy
的答案实际上不会跨越领域(即iframe,web worker,node的vm模块,wasm等)。