--- 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")}}