Get to know MDN better
このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。
この機能は広く実装されており、多くのバージョンの端末やブラウザーで動作します。2016年9月以降、すべてのブラウザーで利用可能です。
Proxy オブジェクトにより別なオブジェクトのプロキシーを作成することができ、そのオブジェクトの基本的な操作に介入したり再定義したりすることができます。
Proxy オブジェクトは、元のオブジェクトの代わりに使用できるオブジェクトを作成することができますが、プロパティの取得、設定、定義などの基本的な Object 操作を再定義することができます。プロキシーオブジェクトは一般的に、プロパティアクセスのログを取ったり、入力の検証、書式化、サニタイズを行ったりするのに使用されます。
Proxy は 2 つの引数で作成されます。
例えば、このコードは target オブジェクトのプロキシーを作成します。
ハンドラーは空なので、このプロキシーは元のターゲットと同様に動作します。
プロキシーをカスタマイズするには、ハンドラーオブジェクトに関数を定義します。
ここで get() ハンドラーを実装し、ターゲットのプロパティへのアクセスに介入します。
ハンドラー関数はトラップと呼ばれることがありますが、これはおそらくターゲットオブジェクトへの呼び出しをトラップするからでしょう。上記の handler2 のトラップは、すべてのプロパティアクセサーを再定義します。
プロキシーは Reflect オブジェクトと共に使用されることが多く、このオブジェクトは Proxy トラップと同じ名前のメソッドをいくつか提供しています。Reflect メソッドは、対応するオブジェクト内部メソッドを呼び出すための反射的な意味づけを提供するものです。例として、オブジェクトの動作を再定義したくない場合、次のように Reflect.get を呼び出すことができます。
Reflect メソッドは、オブジェクトの内部メソッドを通じてオブジェクトとやりとりすることに変わりはありません。プロキシー上で呼び出されても、プロキシーを「脱プロキシー」することはありません。プロキシートラップ内で Reflect メソッドを使用し、Reflect メソッド呼び出しが再びトラップに介入した場合、無限の再帰が発生する可能性があります。
プロキシーの機能について語るとき、以下の用語が使用されます。
ハンドラーProxy コンストラクターの 2 つ目の引数として渡されるオブジェクト。プロキシーの動作を定義するトラップが格納されています。
トラップ対応するオブジェクト内部メソッドの振る舞いを定義する関数です。(これは、オペレーティングシステムにおける「トラップ」の概念に似ています。)
ターゲットプロキシーが仮想化するオブジェクト。プロキシーのストレージバックエンドとして多く使用されます。オブジェクトの非拡張性または設定不可能なプロパティに関するインバリアント(変更されない意味づけ)は、対象に対して検証されます。
不変条件カスタム処理を実装しても変わらない意味づけ。トラップの実装がハンドラーの不変条件に違反する場合、TypeErrorが発生します。
オブジェクトはプロパティの集合体です。しかし、この言語では、オブジェクトに格納されたデータを直接操作するための仕組みは提供されていません。むしろ、オブジェクトは、オブジェクトと対話する方法を指定するいくつかの内部メソッドを定義します。例えば、obj.x を読んだとき、以下のようなことが起こると予想されます。
言語上、この処理について特別なことは何もありません。既定では、普通のオブジェクトがこの動作で定義された [[Get]] 内部メソッドを持っているからに他なりません。obj.x プロパティアクセス構文は、単にオブジェクトの [[Get]] メソッドを呼び出すだけで、オブジェクトは自分自身で内部メソッドの実装を使用して、返す値を決定します。
別の例として、配列は通常のオブジェクトと異なり、魔法のlength プロパティがあり、変更すると自動的に空のスロットに割り当てられたり配列の要素が削除されます。同様に、配列の要素を追加すると、自動的に length プロパティが変更されます。これは、配列には [[DefineOwnProperty]] という内部メソッドがあり、それが、整数の添字の位置に書き込みが行われたときに length を更新したり、length が書き込まれたときに配列の内容を更新したりするということを知っているからです。このような、通常のオブジェクトとは異なる実装を持つ内部メソッドを持つオブジェクトは、エキゾチックオブジェクト と呼ばれます。プロキシーオブジェクトは、自分自身でエキゾチックオブジェクトを定義することができます。
すべてのオブジェクトは、以下の内部メソッドを保持しています。
| [[GetPrototypeOf]] | getPrototypeOf() |
| [[SetPrototypeOf]] | setPrototypeOf() |
| [[IsExtensible]] | isExtensible() |
| [[PreventExtensions]] | preventExtensions() |
| [[GetOwnProperty]] | getOwnPropertyDescriptor() |
| [[DefineOwnProperty]] | defineProperty() |
| [[HasProperty]] | has() |
| [[Get]] | get() |
| [[Set]] | set() |
| [[Delete]] | deleteProperty() |
| [[OwnPropertyKeys]] | ownKeys() |
また、関数オブジェクトは以下の内部メソッドも保有します。
| [[Call]] | apply() |
| [[Construct]] | construct() |
オブジェクトとやりとりするものはすべて、最終的にはこれらの内部メソッドの呼び出しに帰結し、それらはすべてプロキシーによってカスタマイズ可能であることを理解することは重要です。つまり、ほぼすべての動作が(ある重要な不変条件を除いて)言語で保証されているわけではなく、すべてがオブジェクト自身によって定義されるのです。delete obj.x を実行したとき、その後 "x" in obj が false を保証しているわけではありません。それはオブジェクトの [[Delete]] と [[HasProperty]] の実装に依存します。delete obj.x はコンソールにログ出力したり、グローバルプロパティを変更したり、あるいは既存のプロパティを削除する代わりに新しいプロパティを定義することもできますが、これらの意味づけは自分のコードでは避けるべきでしょう。
内部メソッドはすべて言語自身によって呼び出されるものであり、JavaScript コードで直接アクセスすることはできません。Reflect 名前空間は、いくつかの入力正規化/検証の他に、内部メソッドを呼び出す以上のことはほとんどしないメソッドを提供しています。それぞれのトラップのページでは、トラップを呼び出す代表的な場面をいくつか挙げていますが、これらの内部メソッドは、多くの場所で呼び出されます。例えば、配列メソッドはこれらの内部メソッドを通して配列に読み書きするので、push() などのメソッドは get() や set() トラップも呼び出します。
内部メソッドのほとんどは、何をするかは簡単です。混乱しそうなのは [[Set]] と [[DefineOwnProperty]] の 2 つだけです。通常のオブジェクトの場合、前者はセッターを呼び出しますが、後者は呼びません。(また、[[Set]] は既存のプロパティがない場合やプロパティがデータプロパティの場合は内部で [[DefineOwnProperty]] を呼び出します。)obj.x = 1 の構文が [[Set]] を使用し、Object.defineProperty() が [[DefineOwnProperty]] を使用することは知っているかもしれませんが、他にも組み込みメソッドや構文がどのような意味で使用するかはすぐにわかることではありません。例えば、クラスフィールドは [[DefineOwnProperty]] の意味を使用しており、そのため派生クラスでフィールドを宣言しても、スーパークラスで定義されているセッターは呼び出されません。
新しい Proxy オブジェクトを生成します。
メモ: Proxy.prototype プロパティはないので、Proxy インスタンスには特別なプロパティやメソッドはありません。
取り消し可能な Proxy オブジェクトを生成します。
この例では、与えられたプロパティ名がオブジェクトに存在しない場合、既定値である 37 を返します。ここでは get() ハンドラーを使用しています。
この例では、プロキシーが、それに対して適用されるすべての操作を転送する先に、ネイティブの JavaScript オブジェクトを使っています。
この「何もしないプロキシー」はプレーンな JavaScript オブジェクトに対してはうまくいきますが、DOM 要素、Map オブジェクトなどのネイティブオブジェクト、あるいは内部スロットを持つ何かのオブジェクトに対しては機能しないことに注意してください。詳細はプライベートフィールドは転送できないを参照してください。
プロキシーは、やはり異なるアイデンティティを持つ別のオブジェクトであり、ラップされたオブジェクトと外部との間を運営する プロキシー です。そのため、プロキシーは元オブジェクトのプライベート要素に直接アクセスすることができません。
これは、プロキシーの get トラップを呼び出すと、this の値が元の secret ではなく proxy になるため、#secret にはアクセスできないためです。これを修正するには、元の secret を this として使用してください。
メソッドの場合、これはメソッドの this 値も元オブジェクトにリダイレクトしなければならないことを意味しています。
JavaScript のネイティブオブジェクトの中には、内部スロット という、JavaScript コードからはアクセスできないプロパティを持つものがあります。例えば、Map オブジェクトは [[MapData]] という内部スロットを持っており、これはマップのキーと値のペアを格納しています。そのため、マップの転送プロキシーを些細なことで作成することはできません。
このことを回避するためには、上で示した "this-recovering" プロキシーを使用する必要があります。
Proxy を使うと、オブジェクトに渡された値を簡単に検証できます。この例では set() ハンドラーを使用しています。
この例では、2 つの異なる要素の属性を切り替えるために Proxy を使用しています。つまり、一方の要素で属性を設定すると、他の 2 種類の要素では属性が解除されます。
selected プロパティを持つオブジェクトのプロキシーである view オブジェクトを作成します。プロキシーハンドラーは set() ハンドラーを定義します。
HTML 要素を view.selected に割り当てるとき、その要素の 'aria-selected' 属性は true に設定されます。その後、異なる要素を view.selected に割り当てる場合、この要素の 'aria-selected' 属性は true に設定され、前回の要素の 'aria-selected' 属性は自動的に false に設定されます。
この products プロキシーオブジェクトは、渡された値を評価し、必要であれば配列に変換します。また、 latestBrowser という追加プロパティをゲッターとセッターの両方でサポートしています。
| ECMAScript® 2027 Language Specification # sec-proxy-objects |
ブラウザー互換性一覧表を表示するには、JavaScript を有効にしてください。
より良いインターネットのための青写真。
このコンテンツの一部は、©1998–2026 個人の mozilla.org 協力者です。コンテンツはクリエイティブ・コモンズ・ライセンスのもとで利用できます。