--- title: メタプログラミング slug: Web/JavaScript/Guide/Meta_programming tags: - ECMAScript 2015 - Guide - JavaScript - Proxy - Reflect - 'l10n:priority' translation_of: Web/JavaScript/Guide/Meta_programming ---
ECMAScript 2015 から、JavaScript には {{jsxref("Proxy")}} オブジェクトと {{jsxref("Reflect")}} オブジェクトがサポートされました。これらは基本的な言語操作(例えば、プロパティ検索、代入、列挙、関数呼び出しなど)に割り込み、動作をカスタマイズできます。この 2 つのオブジェクトのおかげで、JavaScript でメタレベルのプログラミングが行えます。
ECMAScript 6 で導入された {{jsxref("Proxy")}} オブジェクトによって、特定の操作に割り込んで動作をカスタマイズできます。
例えば、オブジェクトのプロパティを取得してみましょう :
let handler = { get: function(target, name) { return name in target? target[name] : 42 } } let p = new Proxy({}, handler) p.a = 1 console.log(p.a, p.b) // 1, 42
この Proxy
オブジェクトは target
(ここでは空オブジェクト)と get
トラップが実装された handler
オブジェクトを定義しています。ここで、プロキシとなったオブジェクトは未定義のプロパティを取得しようとした時 undefined
を返さず、代わりに数値 42 を返します。
さらなる使用例がリファレンスの「{{jsxref("Proxy")}}」ページにあります。
プロキシの機能について話題にする際は、次の用語が使用されます。
次の表は、Proxy
オブジェクトに対して利用可能なトラップをまとめたものです。詳細と例についてはリファレンスのハンドラについてのページをご覧ください。
ハンドラ / トラップ | 割り込みされる処理 | 不変条件 |
---|---|---|
{{jsxref("Global_Objects/Proxy/handler/getPrototypeOf", "handler.getPrototypeOf()")}} | {{jsxref("Object.getPrototypeOf()")}} {{jsxref("Reflect.getPrototypeOf()")}} {{jsxref("Object/proto", "__proto__")}} {{jsxref("Object.prototype.isPrototypeOf()")}} {{jsxref("Operators/instanceof", "instanceof")}} |
|
{{jsxref("Global_Objects/Proxy/handler/setPrototypeOf", "handler.setPrototypeOf()")}} | {{jsxref("Object.setPrototypeOf()")}} {{jsxref("Reflect.setPrototypeOf()")}} |
target が拡張不可の場合、prototype パラメータは Object.getPrototypeOf(target) と同じ値である必要があります。 |
{{jsxref("Global_Objects/Proxy/handler/isExtensible", "handler.isExtensible()")}} | {{jsxref("Object.isExtensible()")}} {{jsxref("Reflect.isExtensible()")}} |
Object.isExtensible(proxy) は Object.isExtensible(target) と同じ値を返す必要があります。 |
{{jsxref("Global_Objects/Proxy/handler/preventExtensions", "handler.preventExtensions()")}} | {{jsxref("Object.preventExtensions()")}} {{jsxref("Reflect.preventExtensions()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}} | {{jsxref("Object.getOwnPropertyDescriptor()")}} {{jsxref("Reflect.getOwnPropertyDescriptor()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/defineProperty", "handler.defineProperty()")}} | {{jsxref("Object.defineProperty()")}} {{jsxref("Reflect.defineProperty()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/has", "handler.has()")}} | プロパティの照会 :foo in proxy 継承されたプロパティの照会 : foo in Object.create(proxy) {{jsxref("Reflect.has()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/get", "handler.get()")}} | プロパティへのアクセス :proxy[foo] and proxy.bar 継承されたプロパティアクセス : Object.create(proxy)[foo] {{jsxref("Reflect.get()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/set", "handler.set()")}} | プロパティへの代入 :proxy[foo] = bar , proxy.foo = bar 継承されたプロパティの割り当て : Object.create(proxy)[foo] = bar {{jsxref("Reflect.set()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/deleteProperty", "handler.deleteProperty()")}} | プロパティの削除 :delete proxy[foo] , delete proxy.foo {{jsxref("Reflect.deleteProperty()")}} |
ターゲットオブジェクトに設定不可の所有プロパティとして存在する場合、削除することはできません。 |
{{jsxref("Global_Objects/Proxy/handler/enumerate", "handler.enumerate()")}} | プロパティの列挙 / for...in :for (var name in proxy) {...} {{jsxref("Reflect.enumerate()")}} |
enumerate メソッドはオブジェクトを返す必要があります。 |
{{jsxref("Global_Objects/Proxy/handler/ownKeys", "handler.ownKeys()")}} | {{jsxref("Object.getOwnPropertyNames()")}} {{jsxref("Object.getOwnPropertySymbols()")}} {{jsxref("Object.keys()")}} {{jsxref("Reflect.ownKeys()")}} |
|
{{jsxref("Global_Objects/Proxy/handler/apply", "handler.apply()")}} | proxy(..args) {{jsxref("Function.prototype.apply()")}} and {{jsxref("Function.prototype.call()")}} {{jsxref("Reflect.apply()")}} |
handler.apply メソッドに対する不変条件はありません。 |
{{jsxref("Global_Objects/Proxy/handler/construct", "handler.construct()")}} | new proxy(...args) {{jsxref("Reflect.construct()")}} |
出力結果は Object とする必要があります。 |
Proxy
{{jsxref("Proxy.revocable()")}} メソッドは取り消し可能な Proxy
オブジェクトの生成に使用されます。これにより、プロキシを revoke
関数で取り消し、プロキシの機能を停止することができます。
その後はプロキシを通じたいかなる操作も {{jsxref("TypeError")}} になります。
let revocable = Proxy.revocable({}, { get: function(target, name) { return '[[' + name + ']]' } }) let proxy = revocable.proxy console.log(proxy.foo) // "[[foo]]" revocable.revoke() console.log(proxy.foo) // TypeError が発生 proxy.foo = 1 // TypeError が再び発生 delete proxy.foo // TypeError がここでも発生 typeof proxy // "object" が返され, typeof はどんなトラップも引き起こさない
{{jsxref("Reflect")}} は JavaScript で割り込み操作を行うメソッドを提供するビルトインオブジェクトです。そのメソッドは {{jsxref("Global_Objects/Proxy/handler","Proxy ハンドラ","","true")}}のメソッドと同じです。
Reflect
は関数オブジェクトではありません。
Reflect
はハンドラからターゲット
へのデフォルト操作を転送するのに役立ちます。
例えば、{{jsxref("Reflect.has()")}} を使えば、in
演算子を関数として使うことができます :
Reflect.has(Object, 'assign') // true
apply
関数ES5 では、所定の this
値と配列や配列様のオブジェクトとして提供される arguments
を使って関数を呼び出す {{jsxref("Function.prototype.apply()")}} メソッドがよく使われてきました。
Function.prototype.apply.call(Math.floor, undefined, [1.75])
{{jsxref("Reflect.apply")}} を使えば、より簡潔で分かりやすいものにできます :
Reflect.apply(Math.floor, undefined, [1.75]) // 1 Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]) // "hello" Reflect.apply(RegExp.prototype.exec, /ab/, ['confabulation']).index // 4 Reflect.apply(''.charAt, 'ponies', [3]) // "i"
成功した時はオブジェクトを返し、失敗した時は {{jsxref("TypeError")}} を発生させる {{jsxref("Object.defineProperty")}} では、プロパティを定義する際に発生するエラーを捉えるのに {{jsxref("Statements/try...catch","try...catch")}} ブロックを使おうとしていたでしょう。{{jsxref("Reflect.defineProperty")}} では成功したかどうかによって真偽値を返すので、ここでは {{jsxref("Statements/if...else","if...else")}} ブロックを使えます :
if (Reflect.defineProperty(target, property, attributes)) { // 成功した時の処理 } else { // 失敗した時の処理 }
{{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}