web-dev-qa-db-ja.com

オブジェクトがプロキシかどうかをテストする方法は?

JavaScriptオブジェクトが Proxy かどうかをテストしたいと思います。些細なアプローチ

if (obj instanceof Proxy) ...

ここでは機能しません。また、Proxy.prototypeのプロトタイプチェーンをたどることもできません。関連するすべての操作は、基礎となるターゲットによって事実上サポートされているためです。

任意のオブジェクトがプロキシかどうかをテストすることは可能ですか?

33
GOTO 0

私の現在のプロジェクトでは、主にプロキシでプロキシを開始したくないため、何かがすでにプロキシであるかどうかを定義する方法も必要でした。このために、ハンドラーにゲッターを追加しました。これは、要求された変数が「__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;
}

最良の解決策ではないかもしれませんが、それはエレガントな解決策であり、シリアル化するときにもポップアップしないと思います。

22
Xabre

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

オブジェクトがプロキシかどうかを判断することはできません(透過仮想化)。

13
user663031

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
10
Ally

新しいシンボルを作成します。

let isProxy = Symbol("isProxy")

プロキシハンドラのgetメソッド内で、keyがシンボルであるかどうかを確認してから、return true

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

    // normal get handler code here
}

次に、次のコードを使用して、オブジェクトがプロキシの1つであるかどうかを確認できます。

if (myObject[isProxy]) ...
7
David Callanan

実際、オブジェクトがプロキシかどうかを判断するための回避策がありますが、これはいくつかの仮定に基づいています。第一に、プロキシの決定はnode.jsページが安全でない拡張機能を起動できる場合、ブラウザのC++拡張機能または特権Webページを介した環境。第二に、プロキシは比較的新しい機能であるため、古いブラウザには存在しないため、ソリューションは最新のブラウザでのみ機能します。

JSエンジンは関数を複製できません(アクティベーションコンテキストへのバインドおよびその他の理由があるため)が、定義によるプロキシオブジェクトはラッパーハンドラーで構成されます。そのため、オブジェクトがプロキシかどうかを判断するには、オブジェクトのクローンを強制的に開始するだけで十分です。 Inは postMessage 関数で実行できます。

オブジェクトがプロキシの場合、関数が含まれていなくてもコピーできません。たとえば、EdgeおよびChromeは、プロキシオブジェクトをポストしようとするときに次のエラーを生成します:[object DOMException]: {code: 25, message: "DataCloneError", name: "DataCloneError"}およびFailed to execute 'postMessage' on 'Window': [object Object] could not be cloned.

4
Vladislav Ihost

私が見つけた最良の方法は、プロキシオブジェクトの弱いセットを作成することです。プロキシオブジェクトを構築およびチェックしているときに、これを再帰的に実行できます。

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

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

Matthew BrichacekとDavid Callananは、自分で作成したProxyに対して良い答えを出しますが、そうでない場合は、いくつか追加します

変更できないプロキシを作成する外部関数があると想像してください

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

外部コードを実行する前に、プロキシコンストラクターを再定義し、WeakSetを使用して、Matthew Brichacekと同様にプロキシを格納できます。そうしないと、プロキシにプロトタイプがあり、プロキシが変更されたことを検出できるため、クラスを使用しません。

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

質問の目的はこれを回避することでしたが、2番目はプロキシのプロキシを作成するのに対して、コンストラクタを変更するだけなので、最初の方が優れていると思います。

現在のフレームに対してプロキシを再定義しているだけなので、プロキシがiframe内で作成された場合は機能しません。

2
bormat

標準的な方法はないようですが、Firefoxの特権コードには

Components.utils.isProxy(object);

例えば:

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

JS言語仕様に従って、何かがProxyであるかどうかを検出することは不可能です。

nodeはネイティブコードを介してメカニズムを提供しますが、その使用はお勧めしません。何かがProxyであるかどうかを知る必要はありません。

グローバルProxyをラップまたはシャドウすることを示唆するその他の回答は、実際にはクロスレルム(つまり、iframe、Webワーカー、ノードのvmモジュール、wasmなど)では機能しません。

1
LJHarb

アイテムがプロキシかどうかを確認するより安全な方法を見つけたと思います。この答えは Xabreの答え に触発されました。

_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;。この方法で、for-inループまたは_Object.keys_または_Object.hasOwnProperty_を使用している場合、__ isProxyは存在しません。

残念なことに、___isProxy_の値を設定できたとしても、ゲッターのチェックにより、それを取得することはできません。したがって、フィールドが設定されたときにエラーをスローする必要があります。

___isProxy_を別のプロパティとして使用する可能性が高いと思われる場合は、Symbolを使用して変数がプロキシかどうかを確認することもできます。

最後に、プロキシのターゲットにも同様の機能を追加しましたが、これも取得が非常に難しい場合があります。

0
nick zoum