HackerNews翻訳してみた

HackerNewsなどで人気の英語記事を翻訳してお届けします。記事は元記事の著者に許可をとった上で翻訳、掲載をしています。

JavaScriptでbind()を使って部分適用する

Original article: Partial Application in JavaScript using bind() by Pascal Hartig


JavaScriptの中にはコードをもっとシンプルで見やすくできるパターンがあるのに、あまり使われていないものがあります。皆さんもFunction.prototype.bindはご存じでしょう。頻繁に使われていたvar that = thisvar self = thisの代わりになる関数です。よくあるのが以下のような例です。

this.setup = function () {
   this.on('event', this.handleEvent.bind(this));
};

第1引数がbind(束縛)され、返される関数内でthisとして働きます。あまり知られていませんがbindは複数の仮引数を取ることができ、bindされた関数が呼び出されるとbindされる後続のすべての仮引数は、その仮引数リストの前に付加されます。

つまり以下のように、関数を部分適用することができるのです。

var add = function (a, b) {
  return a + b;
};
var add2 = add.bind(null, 2);
add2(10) === 12;

すごいでしょう。冒頭に例として挙げたイベント処理コードを拡張する場合など、この利点がよく分かります。他にもイベント処理の一般的なパターンとしては、ハンドラを呼び出す時にコンテンツを指定するというのがあります。

this.setup = function () {
  this.on('tweet', function (e, data) {
    this.handleStreamEvent('tweet', e, data);
  }.bind(this));
  this.on('retweet', function (e, data) {
    this.handleStreamEvent('retweet', e, data);
  }.bind(this));
};

仮にtweetretweetイベントハンドラが似かよった論理構造の場合、このようにコードを書くのもいいでしょう。ただし欠点は一目瞭然で、ボイラープレートコード(似ているのに省略できないお決まりのコード断片)だらけです。両方に無名関数を用意しなければなりませんし、それぞれ内部でイベントハンドラを呼び出して引数を受け渡し、関数をbindしてthisコンテキストをきちんと設定しないといけません。

もう少しシンプルにできないものでしょうか? もちろんできますよ。

this.setup = function () {
  this.on('tweet', this.handleStreamEvent.bind(this, 'tweet'));
  this.on('retweet', this.handleStreamEvent.bind(this, 'retweet'));
};

これならスッキリしますね。無名関数内で関数を呼び出す代わりに部分適用された関数を2つ用意し、thisコンテキストとそれぞれ異なる第1仮引数を両方に指定しました。もちろんedataも問題なく渡されます。

もし皆さんが数カ月前の私と同じなら、ショックで自分の書いたコードから似たような箇所を探してきてはクリーンアップしたくなることでしょう。その作業が終わったら、このことを友達にも教えてあげてください。