Movatterモバイル変換


[0]ホーム

URL:


MDN Web Docs

このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。

メタプログラミング

Proxy およびReflect オブジェクトにより、基本的な言語操作 (例えば、プロパティ参照、代入、列挙、関数呼び出しなど) に割り込み、動作をカスタマイズすることができます。この 2 つのオブジェクトのおかげで、 JavaScript でメタレベルのプログラミングが行えます。

プロキシー

Proxy オブジェクトによって、特定の操作に割り込んで動作をカスタマイズすることができます。

例えば、オブジェクトのプロパティを取得してみましょう。

js
const handler = {  get(target, name) {    return name in target ? target[name] : 42;  },};const p = new Proxy({}, handler);p.a = 1;console.log(p.a, p.b); // 1, 42

このProxy オブジェクトはtarget (ここでは空オブジェクト) とhandler オブジェクトを定義し、その中にget トラップが実装されています。ここで、プロキシーとなったオブジェクトは未定義のプロパティを取得しようとした時にundefined を返さず、代わりに数値42 を返します。

それ以外の例はProxy のリファレンスページを参照してください。

用語集

プロキシーの機能について話題にする際は、次の用語が使用されます。

ハンドラー (handler)

トラップを入れるためのプレースホルダ用オブジェクト。

トラップ (trap)

プロパティへのアクセスを提供するメソッドです。 (オペレーティングシステムにおけるトラップの概念と同じようなものです。)

ターゲット (target)

プロキシーが仮想化するオブジェクトです。多くの場合、プロキシーのストレージバックエンドとして使用されます。拡張や設定できないオブジェクトのプロパティの不変条件(変更されない意味)がターゲットに対して検証されます。

不変条件 (invariant)

独自の操作を実装した際に変更されない意味を不変条件と呼びます。ハンドラーの不変条件に違反した場合、TypeError が発生します。

ハンドラーとトラップ

次の表は、Proxy オブジェクトに対して利用可能なトラップをまとめたものです。詳細な説明と例については、リファレンスページを参照してください。

ハンドラー / トラップ割り込みされる処理
handler.getPrototypeOf()Object.getPrototypeOf()
Reflect.getPrototypeOf()
__proto__
Object.prototype.isPrototypeOf()
instanceof
handler.setPrototypeOf()Object.setPrototypeOf()
Reflect.setPrototypeOf()
handler.isExtensible()Object.isExtensible()
Reflect.isExtensible()
handler.preventExtensions()Object.preventExtensions()
Reflect.preventExtensions()
handler.getOwnPropertyDescriptor()Object.getOwnPropertyDescriptor()
Reflect.getOwnPropertyDescriptor()
handler.defineProperty()Object.defineProperty()
Reflect.defineProperty()
handler.has()
プロパティの照会
foo in proxy
継承されたプロパティの照会
foo in Object.create(proxy)
Reflect.has()
handler.get()
プロパティへのアクセス
proxy[foo]
proxy.bar
継承されたプロパティへのアクセス
Object.create(proxy)[foo]
Reflect.get()
handler.set()
プロパティへの代入
proxy[foo] = bar
proxy.foo = bar
継承されたプロパティへの代入
Object.create(proxy)[foo] = bar
Reflect.set()
handler.deleteProperty()
プロパティの削除
deleteproxy[foo]
deleteproxy.foo
Reflect.deleteProperty()
handler.ownKeys()Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
Reflect.ownKeys()
handler.apply()proxy(..args)
Function.prototype.apply() およびFunction.prototype.call()
Reflect.apply()
handler.construct()new proxy(...args)
Reflect.construct()

取り消し可能Proxy

Proxy.revocable() メソッドは取り消し可能なProxy オブジェクトの生成に使用されます。これにより、プロキシーをrevoke 関数で取り消し、プロキシーの機能を停止することができます。

その後はプロキシーを通じたいかなる操作もTypeError になります。

js
const revocable = Proxy.revocable(  {},  {    get(target, name) {      return `[[${name}]]`;    },  },);const proxy = revocable.proxy;console.log(proxy.foo); // "[[foo]]"revocable.revoke();console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revokedproxy.foo = 1; // TypeError: Cannot perform 'set' on a proxy that has been revokeddelete proxy.foo; // TypeError: Cannot perform 'deleteProperty' on a proxy that has been revokedconsole.log(typeof proxy); // "object" が返され, typeof はどんなトラップも引き起こさない

リフレクション

Reflect は JavaScript で割り込み操作を行うメソッドを提供する組み込みオブジェクトです。そのメソッドはプロキシーのハンドラーのメソッドと同じです。

Reflect は関数オブジェクトではありません。

Reflect はハンドラーからターゲットへの既定の操作を転送するのに役立ちます。

例えば、Reflect.has() を使えば、in 演算子を関数として使うことができます。

js
Reflect.has(Object, "assign"); // true

より優れた apply() 関数

Reflect が登場する前は、所定のthis 値と配列や配列風オブジェクトとして提供されるarguments を使って関数を呼び出すFunction.prototype.apply() メソッドがよく使われてきました。

js
Function.prototype.apply.call(Math.floor, undefined, [1.75]);

Reflect.apply を使えば、より簡潔で分かりやすいものにできます。

js
Reflect.apply(Math.floor, undefined, [1.75]);// 1Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);// "hello"Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;// 4Reflect.apply("".charAt, "ponies", [3]);// "i"

プロパティ定義の成否チェック

Object.defineProperty は成功すればオブジェクトを返し、そうでなければTypeError が発生するので、try...catch ブロックを使って、プロパティの定義中に発生したエラーを捕捉します。Reflect.defineProperty() は成功のステータスを論理値で返すので、ここではif...else ブロックを使うだけでよいのです。

js
if (Reflect.defineProperty(target, property, attributes)) {  // 成功した時の処理} else {  // 失敗した時の処理}

Help improve MDN

Learn how to contribute.

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp