Slip Ahead Logging

It's not your fault at all.

uncurryThis の仕組み

uncurryThis

最近話題となっている uncurryThis 関数の仕組みが寝転がっている限りでは理解できなかったので,この寒い中を布団からはいだして手を動かした.

uncurryThis を知らないという御仁は以下の記事をご覧頂ければ.

さて uncurryThis といえば,以下のような定義.

var bind = Function.prototype.bind;

var uncurryThis = bind.bind(bind.call);

var every = uncurryThis(Array.prototype.every);
console.log(every([1, 2, 3, 4, 5], function (elem) { return elem < 10; }));

実は,この uncurryThis はできるだけ定義が短くなるように工夫されて書かれているため,それを素直で冗長なかたちに変形する.

var uncurryThis = Function.prototype.bind.bind(Function.prototype.call);

この定義を,順に解読していくことにしよう.

Function.prototype.bind の this 値を束縛

まず,ここで行われているのは bind というメソッドの呼び出しであり,そのレシーバは「Function.prototype.bind」.引数は「Function.prototype.call」だ.

ここで bind メソッドの挙動を復習する.http://es5.github.com/#x15.3.4.5 をあたれば良いだろう.さきほど私が書いた説明を以下に引用しておく.

bind は関数に this や引数の値を束縛するために用いられます.
この bind は Function.prototype.bind というメソッドとして用意されており,

 someFunction.bind(thisArg, arg1, arg2);

などとして使います.

ここで,必要最低限 bind の内部で何が起こっているのかを確認しておきます.まず bind は this の値をターゲットとなる関数として使います.上の someFunction が this の値となっていますね.

bind は this の値 (e.g., someFunction) からネイティブの関数を生成します.このネイティブ関数は基本的に元の関数 omeFunction と同じ挙動をしますが,内部での this 値として bind に渡されていた thisArg,引数として arg1, arg2, ... が用いられる点が異なります(その他にも Code 内部属性が設定されないために toSource, toString でコードが覗けないなどの違いもあります)

覚えておけばよいのは,bind メソッドの対象となる関数は「その関数コンテキスト内での this 値」であり,「その関数に束縛される this 値」は bind の第一引数 thisArg であるということだ.

さてここで再び uncurryThis に立ち返る.

var uncurryThis = Function.prototype.bind.bind(Function.prototype.call);

以上の bind メソッドの挙動に従えば,ここでは「Function.prototype.bind」が bind の対象となっており(メソッド呼び出し形式となっているため bind のコンテキスト内で this となる),その this 値として「Function.prototype.call」が束縛されている.

つまり,この結果として返る uncurryThis 関数は,this 値として「Function.prototype.call」の束縛された「Function.prototype.bind」だ.正体見たり,といったところだろうか.

ここで Function.prototype.bind(thisArg, ...args) は「その関数コンテキスト内で this 値となっている関数」をターゲットとし,その関数の this 値として thisArg を束縛することを思い出す.

すると uncurryThis(thisArg, ...args) という呼び出しが Function.prototype.call の this 値として thisArg を束縛することが理解できるはずだ.uncurryThis は本質的に Function.prototype.bind の呼び出しであるが,その this 値が Function.prototype.call へと束縛されているため,こういった挙動となる.

あとがき

とまあ,メモにしては丁寧な記事をこしらえてみたわけだが,見返してみると冗長で分かりにくい.

追記

一年経って読み返してみたが,なんとまあ分かりにくいことか.uncurryThis を使うと receiver.method(...args) を method_uncurried(receiver, ...args) にすることができる,ということについて,一言も触れていない.