Meta programming

by 1 contributor:

This article is in need of an editorial review.

This translation is incomplete. Please help translate this article from English.

ECMAScript第6版で開始し、JavaScriptはProxyオブジェクトとReflectオブジェクトに対してサポートが増えていて、基本的な言語操作(例えば、プロパティ検索、割り当て、列挙、関数呼び出しなど)に対してカスタム動作をインターセプトして定義することができます。これらの二つのオブジェクトの助けで、JavaScriptのメタレベルでプログラムすることができます。

Proxies

ECMAScript第6版で導入され、Proxy オブジェクトによって、特定の操作を傍受すると、カスタム動作を実装することができます。例えば、オブジェクトのプロパティを取得します:

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

Proxyオブジェクトはtarget(ここではオブジェクト)とget trapが実装されているhandlerオブジェクトを定義します。ここでは、undefinedプロパティを取得するときプロキシされているオブジェクトはundefinedを返しません。しかし、数値42をかわりに返します。

追加の例がProxyリファレンスページにあります。

専門用語

プロキシの機能性について話すとき、次の用語が使用されます。

handler
トラップが含まれているプレースホルダオブジェクト。
traps
プロパティへのアクセスを提供するメソッド。これは、オペレーティング·システムにおけるトラップの概念に類似しています。
target
プロキシが仮想化するオブジェクト。これは、多くの場合、プロキシのストレージバックエンドとして使用されています。オブジェクトの非拡張性または非設定可能なプロパティについては、(変更されないままのセマンティクス)不変量は目標に照らして検証されます。
invariants
カスタム操作を実装するときに変更されないままのセマンティクスが不変条件と呼ばれます。ハンドラの不変条件に違反した場合、TypeErrorがスローされます。

ハンドラとトラップ

次の表は、Proxyオブジェクトに対して利用可能なトラップをまとめたものです。詳細と例については、reference pagesをご覧ください。

Handler / trap Interceptions Invariants
handler.getPrototypeOf() Object.getPrototypeOf()
Reflect.getPrototypeOf()
__proto__
Object.prototype.isPrototypeOf()
instanceof
getPrototypeOf メソッドはオブジェクトかnullを返す必要があります。

targetが拡張可能ではない場合、Object.getPrototypeOf(proxy) メソッドはObject.getPrototypeOf(target)と同じ値を返す必要があります。
handler.setPrototypeOf() Object.setPrototypeOf()
Reflect.setPrototypeOf()

targetが拡張可能ではない場合、prototype パラメータはObject.getPrototypeOf(target)と同じ値である必要があります。

handler.isExtensible()

Object.isExtensible()

Reflect.isExtensible()

Object.isExtensible(proxy)Object.isExtensible(target)を返す必要があります。

handler.preventExtensions()

Object.preventExtensions()

Reflect.preventExtensions()

Object.preventExtensions(proxy)は、Object.isExtensible(proxy)falseの場合、trueを返すだけです。

handler.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor()

Reflect.getOwnPropertyDescriptor()

getOwnPropertyDescriptorはオブジェクトかundefinedを返す必要があります。

プロパティは、ターゲットオブジェクトの設定不可な独自のプロパティとして存在する場合、存在しないと報告することができません。

プロパティは、ターゲットオブジェクトの独自プロパティとして存在しターゲットオブジェクトが拡張可能ではない場合、存在しないと報告することができません。

プロパティは、ターゲットオブジェクトの独自プロパティとして存在せずターゲットオブジェクトが拡張可能ではない場合、存在すると報告することができません。

プロパティは、ターゲットオブジェクトの独自プロパティとして存在しない場合、または、ターゲットオブジェクトの設定可能な独自プロパティとして存在する場合、設定可能ではないと報告することができません。

Object.getOwnPropertyDescriptor(target)の結果はObject.definePropertyを使用してターゲットオブジェクトに適用され。例外をスローしません。

handler.defineProperty()

Object.defineProperty()

Reflect.defineProperty()

プロパティは、ターゲットオブジェクトが拡張可能ではない場合、追加されません。

プロパティは、ターゲットオブジェクトの設定不可な独自プロパティとして存在しない場合、設定不可として追加できず、設定不可に更新できません。

プロパティは、ターゲットオブジェクトの対応する設定可能なプロパティが存在する場合、設定不可ではないです。

プロパティが対応するターゲットオブジェクトプロパティを持つ場合、Object.defineProperty(target, prop, descriptor)は例外をスローしません。

strictモードでは、definePropertyハンドラからのfalseの戻り値はTypeError例外をスローします。

handler.has()

プロパティの照会: foo in proxy

継承されたプロパティの照会: foo in Object.create(proxy)

Reflect.has()

プロパティは、ターゲットオブジェクトの設定不可の独自プロパティとして存在する場合、存在しないと報告することができません。

プロパティは、ターゲットオブジェクトの独自プロパティとして存在し、そのターゲットオブジェクトが拡張可能ではない場合、存在しないと報告することができません。

handler.get()

プロパティアクセス: proxy[foo]and proxy.bar

継承されたプロパティアクセス: Object.create(proxy)[foo]

Reflect.get()

プロパティに対して報告される値は、ターゲットオブジェクトプロパティが書込不可で設定不可のデータプロパティである場合、対応するターゲットオブジェクトプロパティと同じ値である必要があります。

プロパティに対して報告される値は、対応するターゲットオブジェクトプロパティが、その[[Get]]属性としてundefinedを持つ設定不可のアクセスプロパティである場合undefinedである必要があります。

handler.set()

プロパティの割り当て: proxy[foo] = bar and proxy.foo = bar

継承されたプロパティの割り当て: Object.create(proxy)[foo] = bar

Reflect.set()

対応するターゲットオブジェクトプロパティが書込不可で設定不可のデータプロパティである場合、その対応するターゲットオブジェクトプロパティと違うプロパティの値に変更できません。

対応するターゲットオブジェクトプロパティが、その[[Set]]属性としてundefinedを持つ設定不可のアクセスプロパティである場合、プロパティの値を設定できません。

strictモードでは、setハンドラからのfalseの戻り値はTypeError例外をスローします。

handler.deleteProperty()

プロパティの削除: delete proxy[foo] and delete proxy.foo

Reflect.deleteProperty()

プロパティは、ターゲットオブジェクトの設定不可の独自プロパティとして存在する場合、削除されません。
handler.enumerate()

プロパティ 列挙 / for...in: for (var name in proxy) {...}

Reflect.enumerate()

enumerate メソッドはオブジェクトを返す必要があります。
handler.ownKeys()

Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
Reflect.ownKeys()

ownKeysの結果はListです。

各結果List要素の型はStringSymbolのどちらかです。

結果Listはターゲットオブジェクトの設定不可の独自プロパティのキーを含む必要があります。

ターゲットオブジェクトが拡張可能ではない場合、結果Listはターゲットオブジェクトの独自プロパティのキーすべてを含む必要があり、他の値は含みません。

handler.apply()

proxy(..args)

Function.prototype.apply() and Function.prototype.call()

Reflect.apply()

handler.applyメソッドに対して全く不変量がありません。
handler.construct()

new proxy(...args)
Reflect.construct()

結果はObjectになる必要があります。

取消可能なProxy

Proxy.revocable() メソッドは取消可能なProxyオブジェクトを生成するために使用されます。これは、プロキシは、revoke関数で取り消すことができ、プロキシを停止できることを意味します。その後、任意の操作がプロキシにつながり、TypeErrorにつながります。

var revocable = Proxy.revocable({}, {
  get: function(target, name) {
    return "[[" + name + "]]";
  }
});
var proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"

revocable.revoke();

console.log(proxy.foo); // TypeError is thrown
proxy.foo = 1           // TypeError again
delete proxy.foo;       // still TypeError
typeof proxy            // "object", typeof doesn't trigger any trap

リフレクション

Reflectはインターセプト可能なJavaScriptの操作のためのメソッドを提供するビルトインオブジェクトです。そのメソッドはproxy handlersのメソッドと同じです。Reflectは関数オブジェクトではありません。

Reflectはハンドラからターゲットへのデフォルト操作を転送するのに役立ちます。ReflectはまだFirefoxでは実装されていないことに注意してください。

例えば、Reflect.has()を用いて、関数としてin operatorを手に入れます。:

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

ドキュメントのタグと貢献者

Contributors to this page: shide55
最終更新者: shide55,
サイドバーを隠す