ナカザンドットネット

それって私の感想ですよね

JSはbindで関数の部分適用ができる

JS初心者の備忘録です。

お仕事でJS向けのAPIクライアントライブラリのラッパーを作り始めた関係で、Promiseと戦っています。

Promiseといえば、promise-bookですね。

azu.github.io

azuさんには足を向けて寝られません。

ところで、promise-bookには、次のようなサンプルがあります。

function sequenceTasks(tasks) {
    function recordValue(results, value) {
        results.push(value);
        return results;
    }
    var pushValue = recordValue.bind(null, []);
    return tasks.reduce(function (promise, task) {
        return promise.then(task).then(pushValue);
    }, Promise.resolve());
}

pushValueの生まれ方と使われ方が気になります。

  • 既存の知識
    • thenに入れられるということは、引数をひとつ取る関数であるはず
    • bindを関数Xに適用すると、第一引数に入れたthisが適用先の関数Xでthisとして扱われるようになる・・・という機能の他に、第二引数以降が関数Xの引数として適用される
  • recordValueは引数をふたつ取る関数なのに、bindでは[]のひとつしか渡していない

これらの条件から、pushValueは「recordValueの第一引数に[]を部分適用した関数」なのかなと想像しました。

と、ここまで何も資料を見ずに考察していたのですが、普通にドキュメント見たらいいですよね。

developer.mozilla.org

Partial Functionsの項に、引数の束縛についても書いてありますね。最初からこっち見ておけばよかった。

サンプル

公式ドキュメントを見る前に、bindが想像した通りの動作をするかどうか確認したサンプルです。

var add = function(a, b) {
  return a + b;
}

add(1, 2); // => 3

var addOne = add.bind(null, 1); // 第一引数を1に束縛

addOne(2); // => 3
addOne(0); // => 1

うん、部分適用されていますね。

まとめ

JSではbindを使うと関数の部分適用ができる。なかざんおぼえた。

まだまだJS初心者なので、今後もこういう発見がたくさんありそうで楽しみです。

Code SchoolのAngularJS入門がとても良かった

Ionicを勉強する機会があったので、基礎学習として元になっているAngularJSについても勉強してみました。

色々探してみて、最終的に公式サイトのメニューからたどり着いたのがCode Schoolのチュートリアルです。

www.codeschool.com

Code Schoolを利用したのは今回が初めてでしたが、動画レクチャーとハンズオンを交互に行うスタイルはインプットとアウトプットのイテレーションを素早く回せてよいですね。

また、授業の内容も上手に組み立てられていたように思います。一歩ずつ丁寧に説明してくれるので、頭の中にブラックボックスを作らずに理解を積み重ねていくことができました。特にありがたかった点は、一度たりとも$scopeが登場しなかったところです。

$scopeは使わなければならないのか?

続きを読む

TypeScriptの型定義が思ったより柔軟だった楽しい

TypeScript 1.0のリリースを機に、ちょっとJavaScript方面にも手を出してみようかということで、最近はわかめ本を読みながらチマチマとコードを書いております。

TypeScriptリファレンス Ver.1.0対応

TypeScriptリファレンス Ver.1.0対応

今日は技評さんのjQuery記事をTypeScriptで写経しながら読み進めていました。

もちろんjquery.d.tsを使って型チェックを利かせながらの写経です。メソッドがサジェストされるのもなかなか気持ちが良いですが、 ajax(settings: JQueryAjaxSettings) への渡し値であるJQueryAjaxSettingsがしっかり定義されているため、キーがサジェスト&プロパティ存在チェックされるわ、値に型チェックが入るわで、超気持ちいい感じです。

しかし、こうも型で守られた中で写経していると、むしろ型が付いていないところが気になってしまいます。特に今回の写経では、ジェネリクスが利いている each<T>(collection: T[], callback: (indexInArray: number, valueOfElement: T) => any): any; にany型のcollectionを渡してしまうのは、型の確定を心待ちにしているであろう valueOfElement さんにあまりにも申し訳が立たないと感じました。

というわけで、自前でインターフェースによる型定義を行ってみることにしました。YouTubeAPIから返ってくるJSONは、本来ならばそれなりに複雑ですが、今回の例に使う部分だけならば、下記のようなJSONを想定すれば充分でした。

{
  "feed": {
    "entry": [
      {
        "media$group": {
          "media$player": [{"url": "http://hogehoge"}],
          "media$thumbnail": [{"url": "http://fugafuga"}]
        }
      },
      ...
    ]
  }
}

で、実際に写経してみたのが以下になります。

YouTubeResponseFeedまでは真面目に書いていたのですが、途中から面倒くさくなってEntryインターフェースではエイヤッと超適当に書いてみたら通って逆にびっくり。構造的部分型の真骨頂を垣間見たような気がします。

そんなわけで、successに定義した関数は、JavaScript版と変わりなく書かれているように見えますが、

  • ジェネリクスによる型の確定(data.feed.entryEntry[]型であり、itemEntry型である)
  • 型チェック(group.media$playerは配列である)
  • パラメータ存在チェック(group.media$player[0]urlというパラメータを持つ)

のようなことがコンパイル時に行われています。タイポしたり、存在しないメソッドやパラメータを勝手に生やすと速攻で叱られるので、私のようなゆとりプログラマーでも安心してコードを書くことができますね。

まとめ

構造的部分型を活用してインターフェースを定義すると大変捗る!!!

おまけ

むりやり1つのインターフェースにまとめてみた。可読性はクソ悪い*1けど、TypeScriptの型システムとしては、これでもOK。

interface YouTubeResponse {
  feed: {
    entry: Array<{
      media$group: {
        media$player: Array<{url: string;}>;
        media$thumbnail: Array<{url: string;}>;
      };
    }>;
  };
}

*1:慣れるとそこまででもない?