--- 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
(ここでは空オブジェクト) と handler
オブジェクトを定義し、その中に get
トラップが実装されています。ここで、プロキシーとなったオブジェクトは未定義のプロパティを取得しようとした時に undefined
を返さず、代わりに数値 42
を返します。
それ以外の例は {{jsxref("Proxy")}} のリファレンスページを参照してください。
プロキシーの機能について話題にする際は、次の用語が使用されます。
次の表は、 Proxy
オブジェクトに対して利用可能なトラップをまとめたものです。詳細な説明と例については、リファレンスページを参照してください。
ハンドラー / トラップ | 割り込みされる処理 | 不変条件 |
---|---|---|
{{jsxref("Global_Objects/Proxy/Proxy/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/Proxy/setPrototypeOf", "handler.setPrototypeOf()")}} | {{jsxref("Object.setPrototypeOf()")}} {{jsxref("Reflect.setPrototypeOf()")}} |
target が拡張できない場合、prototype パラメータは Object.getPrototypeOf(target) と同じ値である必要があります。 |
{{jsxref("Global_Objects/Proxy/Proxy/isExtensible", "handler.isExtensible()")}} | {{jsxref("Object.isExtensible()")}} {{jsxref("Reflect.isExtensible()")}} |
Object.isExtensible(proxy) は Object.isExtensible(target) と同じ値を返す必要があります。 |
{{jsxref("Global_Objects/Proxy/Proxy/preventExtensions", "handler.preventExtensions()")}} | {{jsxref("Object.preventExtensions()")}} {{jsxref("Reflect.preventExtensions()")}} |
|
{{jsxref("Global_Objects/Proxy/Proxy/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}} | {{jsxref("Object.getOwnPropertyDescriptor()")}} {{jsxref("Reflect.getOwnPropertyDescriptor()")}} |
|
{{jsxref("Global_Objects/Proxy/Proxy/defineProperty", "handler.defineProperty()")}} | {{jsxref("Object.defineProperty()")}} {{jsxref("Reflect.defineProperty()")}} |
|
{{jsxref("Global_Objects/Proxy/Proxy/has", "handler.has()")}} |
|
|
{{jsxref("Global_Objects/Proxy/Proxy/get", "handler.get()")}} |
|
|
{{jsxref("Global_Objects/Proxy/Proxy/set", "handler.set()")}} |
|
|
{{jsxref("Global_Objects/Proxy/Proxy/deleteProperty", "handler.deleteProperty()")}} |
|
target に構成不可の所有プロパティとして存在する場合、削除することはできません。 |
{{jsxref("Global_Objects/Proxy/enumerate", "handler.enumerate()")}} |
|
enumerate メソッドはオブジェクトを返す必要があります。 |
{{jsxref("Global_Objects/Proxy/Proxy/ownKeys", "handler.ownKeys()")}} | {{jsxref("Object.getOwnPropertyNames()")}} {{jsxref("Object.getOwnPropertySymbols()")}} {{jsxref("Object.keys()")}} {{jsxref("Reflect.ownKeys()")}} |
|
{{jsxref("Global_Objects/Proxy/Proxy/apply", "handler.apply()")}} | proxy(..args) {{jsxref("Function.prototype.apply()")}} and {{jsxref("Function.prototype.call()")}} {{jsxref("Reflect.apply()")}} |
handler.apply メソッドに対する不変条件はありません。 |
{{jsxref("Global_Objects/Proxy/Proxy/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/Proxy","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("Object.defineProperty")}} は成功すればオブジェクトを返し、そうでなければ {{jsxref("TypeError")}} を投げるので、 {{jsxref("Statements/try...catch","try...catch")}} ブロックを使って、プロパティの定義中に発生したエラーを捉えます。 {{jsxref("Reflect.defineProperty")}} は成功のステータスを論理値で返すので、ここでは {{jsxref("Statements/if...else","if...else")}} ブロックを使うだけでよいのです。
if (Reflect.defineProperty(target, property, attributes)) { // 成功した時の処理 } else { // 失敗した時の処理 }
{{PreviousNext("Web/JavaScript/Guide/Iterators_and_Generators", "Web/JavaScript/Guide/Modules")}}