俺、サービス売って家買うんだ

Swift, Kotlin, Vue.js, 統計, GCP / このペースで作ってればいつか2-3億で売れるのがポっと出来るんじゃなかろうか

【今更ES2015を追ってみるシリーズ 2】Proxy編

f:id:ie-kau:20151019153301j:plain

不定期連載その2です。
今回はProxyについて調べたのでその辺を書きます。

前回の記事

Proxyとは?

A proxy object is an exotic object whose essential internal methods are partially implemented using ECMAScript code.

出典:ECMAScript 2015 Language Specification – ECMA-262 6th Edition

Proxyオブジェクトはネイティブコードの一部をECMAScriptのコードで実装しなおしたオブジェクトです。 つまりネイティブのコードをProxyを介して一部書き換えることができるということです。(英語表現ではtrapするというのがよく使われていますね。)

何のためにやるか?

BabelのLearn ES2015のページにを読むと目的は以下の様に書かれていました。
全ての対象はネイティブメソッド部分です。

  • interception
    • native実装の関数に対して割り込み処理をいれる
  • object virtualization
    • オブジェクの仮想化(getter/setter)
  • logging/profiling
    • ログを仕込む
  • etc

Proxyが適応できるネイティブのメソッド

  • getPrototypeOf
  • setPrototypeOf
  • isExtensible
  • preventExtensions
  • getOwnPropertyDescriptor
  • has
  • get
  • set
  • deleteProperty
  • defineProperty
  • enumerate
  • ownKeys
  • apply
  • construct

functionもオブジェクトなのでapplyに対してProxyが使えます。

一例(object virtualization)

let handler = {
  set(target, prop, value, receiver) {
    if (prop === 'age' && value < 20) {
      throw new TypeError('Age must be over 20.');
    }

    target[prop] = value;
  }
}

let customer1 = new Proxy({name: 'Tanaka'}, handler);
let customer2 = new Proxy({name: 'Suzuki'}, handler);

customer1.age = 10; // TypeError: Age must be over 20.                                                                                      
customer2.age = 20;

ageのvalidationを含んだハンドラを作成しておきageのvalidationが必要なオブジェクトを作成するときにProxyを利用してsetterを定義します。

一例のソースコードを読み解く

let handler = {
  set(target, prop, value, receiver) {
  }
}

setの引数は以下の通りになります。

  • target
    • new Proxyの第一引数
  • prop
    • プロパティ名
  • value
    • 例でいうcustomer1.ageに渡された値
  • receiver
    • これはnew Proxyで返された後のcustomer1やcustomer2と===になる

よくわからなかったこと

まぁ、色々あるんですが 笑

handler.enumerate() - JavaScript | MDN

let p = new Proxy({}, {
  enumerate(target) {
    console.log(""called);
    return ["a", "b", "c"][Symbol.iterator]();
  }
});

for (let x in p) { // "called"
  console.log(x);  // "a"
}                  // "b"
                   // "c"

上記リンクのMDNのページにfor..inをtrapする方法が載っています。handlerにenumerateを登録してProxyさせるのですがenumerateが返す値はiteratorでなければならないようです。

  • for..inのネイティブ実装がどうなってるのか調べていないので何故ここでiteratorを返さなければならないのか。
    • 内部的にnextがコールされてるの?
  • for..inをtrapしてiteratorを返してループさせる利用ケースとは?
    • はなからiteratableなオブジェクトを作ってfor..ofで回せばいいのではと。。

この辺がいまいちわからないですね。うーむ。

まとめ

Proxyの使い方は理解できたのですが今のところ、

  • getter/setterを便利にする

以外ぱっと利用シーンが思いつかないですね。ネイティブのコードをtrapしまくっても読みづらくなるだけだろうし。
検索してみるとprofilerを作ったりしてる例もごく僅かですが出てくるのでもう少し勉強してみないと。。 ٩(๑❛ᴗ❛๑)۶オラッオラッ

参考

BabelのProxyの項

本家

こんなこともできる

virtual propertyについて

今回調べごとをしてて、virtual propertyという言葉がわからなかったので参考になりました。そのままgetter/setterのことだったけど整理するとこんな感じ。

  • Value Property
    • 通常のプロパティ
  • Virtual Property
    • .アクセスしてるけど本当はgetter/setter