JavaScript
メタプログラミング
es6

Proxy型

ES6 で新しく Proxy型が導入された。

Proxy型は JavaScript のメタプログラミングで多用される。

普通のプログラマはメタプログラミングなんてしないので、基本的に忘れてしまってよい話。でもまあ、知っていても損はしないのでざっくりレビュー。

Proxy型って結局なんなの?

めちゃくちゃざっくりいうと、任意のオブジェクトのゲッターとセッターを魔改造する技術。(それ以外にも new とか for ... in 文を魔改造することもできるけどここでは割愛。)いやいや、JavaScriptのゲッターセッターって何よ、と思うかもしれない。もう少し具体的に言うと、以下のコードの挙動を魔改造するってこと。

  • プロパティアクセス obj.name の際の挙動
  • プロパティへの代入 obj.name = 'alice' の際の挙動

どんな魔改造かって?それは色々。存在しないプロパティアクセスがあった際にundefinedでなくデフォルト値を返すだとか、透過的にRDBMS等リモートサーバに値を取得しにいってその値を返すだとか。値を実際に設定する前にバリデーションチェックをして、だめな場合はエラーを吐くだとか。ようは何でもできるが、Proxy型を使う人にはそのロジックは一切見えない、つまり透過的にできるという技術。

具体例を一つ。代入する際には大文字に勝手に変換して、参照する際には!をつけて取り出す例

const obj = new Proxy({}, {get: (t,n)=>t.n+'!', set: (t,n,v)=>t.n=v.toUpperCase()})
obj.name = 'alice' //ALICE に変換されて代入される
console.log(obj.name) // ALICE! が出力される

アンチパターン?

これってどう考えてもアンチパターンだよね。普通はやらない。かっこつけてメタプログラミングしても技術的負債になるだけ。ES5にもトランスパイルできないらしいので、使うならサーバサイド。

あえて使えそうな用途をあげるとすると、動的なメソッドの生成があげられるかもしれない。後述のライブラリでもそういうのが存在する。つまり、JavaScriptでは関数は第一級なのでオブジェクトのプロパティを関数にできる。つまり、オブジェクトのゲッタに介在して、動的にメソッドを生成してそれを実行する、といったことが可能だ。例えばこんな感じに呼び出せたらクールではない?

arr.findWhereNameEquals('Lily')
arr.findWhereSkillsIncludes('javascript')
arr.findWhereSkillsIsEmpty()
arr.findWhereAgeIsGreaterThan(40)

出典: https://medium.com/dailyjs/how-to-use-javascript-proxies-for-fun-and-profit-365579d4a9f8

Proxy関連のライブラリ

https://github.com/mikaelbr/proxy-fun/ から抜粋。-1番目の配列要素にアクセス可能なのとか、さっき紹介した宣言的なメソッド呼び出し(Declaraoids)とかはおもしろそう。あと、v も気になったけど使っていない。

zer: Zer helps you serialize any JavaScript chains to String representations of any languages by leveraging ES2015 Proxy objects
negative-array: Negative array index support array[-1] using ES2015 Proxy
tpyo: A small script that enables you to make typos in JavaScript property names. Powered by ES6 proxies + Levenshtein string distance.
stevia: Experimental module providing natural sweetening for javascript objects
known: Allow only access to known object properties using ES2015 Proxy
iSeries: A set of modules that use Proxy to extend and enhance your existing JavaScript classes.
proxy-observe: A Proxy based implementation of Object.observe
Experimental Safe JavaScript Navigation : The purpose of this function is to provide a way to avoid deep nested conditionals when traversing a hierarchy of objects
allora: Promisify everything in less tha ~50 lines It can be used to use promises on any javascript object.
stroxy: A simple streaming wrapper for native event functions (e.g. addEventListener) using ES2015 proxies.
Declaraoids: Declarative programming on steroids.
python-range: A JavaScript implementation of the Python's range() function.
rebridge: Transparent Javascript-Redis bridge.
box-js: A tool for studying JavaScript malware.
v: Secure, Synchronized, Realtime, Cloud, Native JavaScript Variables & Events.
Promiser: Promise combination resolver by get accessor pattern.
React Easy State: A transparent state management library for React.
The Observer Utility: A general purpose transparent reactivity library (React Easy State is just a thin wrapper of it). It uses ES6 Proxies to achieve a 100% language coverage for reactivity.
The Compiler Utility: A 'sandboxed' code evaluation library. It uses ES6 Proxies (has traps) in conjuction with the with keyword to 'sandbox' code.

注意

メタプログラミングは用法用量を守ってつかいましょう