一から勉強させてください( ̄ω ̄;)

最下級エンジニアが日々の学びをアウトプットしていくだけのブログです。

JavaScriptのカリー化について

先日underscore.js (http://underscorejs.org/) のbindのところ

_.bind = function(func, context) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(context, args.concat(slice.call(arguments)));
    };
  };

を見ていて、いまいちよくわからなかったのでいろいろ調べてみたんです。


で、これがうわさの「カリー化」というやつだと判明しました!!!
なんか「部分適用できる的なやつでしょ!」ぐらいしか知らなかったので、とりあえず引き続き調べました。


安定のWikipedia

複数の引数をとる関数を、引数が「もとの関数の最初の引数」で戻り値が「もとの関数の残りの引数を取り結果を返す関数」であるような関数にすること


まあ言わんとしてることはなんとなくわかりますが、いまいちピンとこない。。


そこで僕がたまたま所有していたJavascriptパターンという本にカリー化について書かれたところがあったので、その例をみてみました。

こちら↓

function schonfinkelize(fn){
  var slice = Array.prototype.slice,
      stored_args = slice.call(arguments, 1); //0番目はfnなので、除外する
     
  return function(){
    var new_args = slice.call(arguments),
        args = stored_args.concat(new_args);
    return fn.apply(null, args);
  };
}

//使用例
//fnの部分がadd
function add(x, y){
  return x+y;
}
var newadd = schonfinkelize(add,4);  //このとき、arguments → [function, 4],  stored_args →[ 4 ]
newadd(5);  //9


順にみていくと、

まず stored_args = slice.call(arguments, 1); のところですが、
これはargumentsが配列と見せかけて、実は本物の配列ではないから、argumentsでもArrayのメソッドを使えるようにするための処理ですね。

んで引数の2個目に1を指定していますが、
これは0番目が実際にカリー化されるfnであり、1番目からがfnの引数(ここからがsliceの対象になる)だからだと思われます。

そしてreturn function()〜のところで新しい関数を返しています。クロージャだからstored_argsとかもがっつり参照してますね。

あとは新しく渡した引数new_argsとstored_argsをくっつけて、元の関数fnに適用して終了です。


ではもう一度underscoreのbindを見てみます。

_.bind = function(func, context) {
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    var args = slice.call(arguments, 2);
    return function() {
      return func.apply(context, args.concat(slice.call(arguments)));
    };
  };


nativeBindというのはFunction.prototype.bindみたいですね。bindがある場合はそれ使えってことになってます。

こちらの記事を参考にさせていただいたのですが、
http://foreignkey.jp/archives/763


ネイティブのbindでもともと部分適用ができるっぽいですね。thisを束縛するしか使い道知らなかったので、勉強になりました。
これを改めて書いているのがunderscoreの_.bindなんですね。

てか先ほどのschonfinkelizeと大体同じですね。
funcとcontextを渡すので、slice.call(arguments, 2)となっています。


実際になんか例を書いてみます。

//underscoreのbindの使用例

var func = function(a, b){
  return this.name + "さんの年齢は" + (a + b) + "歳です";
};

func = _.bind(func, {name : '花子'}, 30);
func(20);  //"花子さんの年齢は50歳です"


花子さんの年齢は30歳だと聞いていたので、「花子さんの年齢は30歳です」と表示するプログラムを作ろうとしました。
でも、ビジュアル的にどうも信用できません。
                    ↓
そこで部分適用でいつでも調整できるようにしておきます。
                    ↓
すると、やはり実は20歳もサバを読んでやがったことが判明しました。でも心配いりません。
20を引数に渡して調整すれば、ちゃんと「花子さんの年齢は50歳です」 と正確な年齢を表示することができます。            


この上なく無意味な例ですが、こんな感じで部分適用ができるんですね。たぶん使い方としては0点ですw


こちらの記事に書かれていたのですが、
http://qiita.com/items/6a27c0e8efa66b1f7799


カリー化のメリットが最も発揮されるのは、

・関数が最初からカリー化されている
・関数適用の構文がシンプル
高階関数を多用する API が用意されている

という条件が揃った時です


とあります。

たしかに無意味に使うとプログラムをややこしくするだけだし、ちゃんとメリットを確認した上で使っていけたらと思います。


あ、最後に今回、参考にした本載せときます↓

JavaScriptパターン ―優れたアプリケーションのための作法

JavaScriptパターン ―優れたアプリケーションのための作法


小さな事からコツコツと。