ChatGPT解决这个技术问题 Extra ChatGPT

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

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

if (obj instanceof Proxy) ...

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

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

也许代理代理?我的意思是在任何涉及代理的脚本之前代理代理功能..
代理的目的不是您无法将它们与“正常”对象区分开来吗?你为什么要测试这个?
@Bergi 好吧,这当然不是代理的主要目的。剩下的,我有点惊讶你无法找出这个测试的用例。
我写了一个小提琴来澄清这个想法,... jsfiddle.net/ycw7788/uauoxn7o
那行得通吗? function isProxy(o) { if (typeof Proxy !== 'function') return false;尝试 { o instanceof 代理;返回假; } 捕捉 () { 返回真; } }

X
Xabre

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

function _observe(obj) { if (obj.__isProxy === undefined) { var ret = new Proxy(obj || {}, { set: (target, key, value) => { /// 改变返回true; }, get: (target, key) => { if (key !== "__isProxy") { return target[key]; } return true; } });返回 ret; } 返回对象; }

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


谢谢!我正在做某种深度代理,我也遇到了这个问题。
太好了,我正在使用这种方法,但有一点小改动: const IS_PROXY = Symbol("is-proxy"); ...如果(!obj[IS_PROXY])
@MatthewJoelRodríguezLlanos 只需确保符号存储在某个闭包中,因为 Symbol("is-proxy") !== Symbol("is-proxy"),或者您使用 Symbol.for
这仅在您控制创建代理陷阱时才有效。
G
GOTO 0

在 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

D
David Callanan

创建一个新符号:

let isProxy = Symbol("isProxy")

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

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

    // normal get handler code here
}

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

if (myObject[isProxy]) ...

有趣的!只是给其他人的注释,这只有在您控制代理获取陷阱时才有效。否则,如果您不拥有该对象,这将不起作用。
@snewcomer 谢谢。没错,我认为这满足了常见的用例。 (我个人认为您不应该检查外来对象是否是代理,因为那时您正在编写特定于实现的代码并首先覆盖代理的整个抽象性质/目的)。
佚名

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

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


我不会链接以注释“此博客文章已过时”开头的文章 :-) 但是,exploringjs.com/es6/ch_proxies.html 声明完全相同
@BrassApparatus 实际上,事实证明他的评论和我的回答都是错误的,详细的here
A
ADJenks

为 instanceof 代理添加“支持”:

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

(() => { var proxyInstances = new WeakSet() // 可选择将原始文件保存在全局范围内: 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


Yo dawg,我听说你喜欢代理,所以我把代理放在你的代理中,这样你就可以在制作代理后识别代理。
V
Vladislav Ihost

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

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

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


有趣的。我想知道是否有办法让这个方法也适用于包含函数、DOM 元素和其他不可克隆的东西的对象。
+1 用于在许多情况下找到一种真正的解决方案。结果证明,以同样的方式,代理的 Event 对象不能被分派(即 dispatchEvent)并且代理的 DOM 元素不能附加到 DOM。也可能有其他独特的(如音频上下文节点)。
@Codesmith 在想知道为什么代理事件不会发送后,我发现自己在这里。
“所以要确定对象是否是代理,启动强制对象克隆就足够了。”有趣的。我认为根据定义代理应该将函数抽象为看起来只是属性-值对的东西。所以我会认为强制克隆相当于迭代代理中的每个属性并在克隆时克隆它的值。 (因为这对 javascript 用户来说应该是这样的)
@DavidCallanan 听起来像“leaky abstraction
E
Eylon Sultan

使用 window.postMessage() 和 try-catch 来获得提示

postMessage 不能序列化与 structured clone algorithm 不兼容的对象,例如 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()));

有用!有了这个针对浏览器的解决方案和针对 NodeJS 的 this other solution,我们涵盖了两个主要上下文。 :)
这只会告诉您该对象是否不可克隆,对吗?因此,这个非代理从您的函数返回 true:isProxy({a:()=>{}})。它应该被称为“isNotCloneable”。
不适用于 jsdomjest,因为它们具有假 postMessage() 功能
这个答案是不正确的。结构化克隆算法还会为 Function 对象和 DOM 节点抛出 DataCloneError。
shouldBeCloneable({}) 为假是否有原因?在我看来,您应该检查 o?.constructor === ({}).constructor ...
M
Matthew Brichacek

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

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

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

b
bormat

Matthew Brichacek 和 David Callanan 为您自己创建的 Proxy 提供了很好的答案,但如果不是这样,这里有一些补充

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

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

相同的方法,但使用像 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

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

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


这是IMO最实用的解决方案。
O
Oriol

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

Components.utils.isProxy(object);

例如:

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

L
LJHarb

根据 JS 语言规范,无法检测某物是否为 Proxy

node 确实通过本机代码提供了一种机制,但我不建议使用它 - 你不应该知道某物是否是 Proxy

其他建议包装或遮蔽全局 Proxy 的答案实际上不会跨领域工作(即 iframe、网络工作者、节点的 vm 模块、wasm 等)。


u
user943702

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

要监视函数,我们可以编写包装器或使用内置代理。后者表示使用 Proxy 来代理 new Proxy 以及 Proxy.recovable,这里有一个 fiddle 来演示这个想法。

为了像 nodejs-v5.8.0 代理一样为 old Proxy API 提供服务,我们可以通过使用 Proxy.createFunction 代理 Proxy.createProxy.createFunction 来应用相同的想法。


这种方法不适用于在其他领域(如 iframe 或节点的 vm 模块)中创建的代理,因为您无法监视其他领域中的全局变量(如 Proxy)。
@LJHarb 你确定吗?实际上,我在阅读您的评论后尝试过,但在跨 iframe 领域替换 Proxy 对象时没有问题。我没有用 vm 模块测试它,但不明白为什么它不应该以同样的方式工作。
@GOTO0 我的意思是,除非您可以先在其他领域中运行代码,否则您不能保证它具有阴影代理。特别是对于 iframe,我可以创建一个全新的并访问原始的。
@LJHarb 如果是这样,这是一个有效的观点,它同样适用于我在这里看到的大多数其他答案。不幸的是,您选择了最不显眼的答案来发表评论。
我发布了一个答案;如果您觉得它有价值,请随时投票。
n
nick zoum

我相信我找到了一种更安全的方法来检查该项目是否为代理。此答案的灵感来自 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 作为不同的属性。

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