Node.jsの初見殺し (x) => (y) => { ... }
引数定義する部分が2つある関数定義、まさに初見殺し。
const a = (x) => (y) => { console.log(x * y) }
ただでさえ function (x) { ... }
のシンタックスシュガーみたいな (x) => { ... }
(アロー関数)があったりするのに、アロー関数には引数定義が2つ付いた (x) => (y) => { ... }
みたいな定義方法もある。調べても情報が多くないように思うし、そもそも調べ方もよく分からないかんじがある。この記事はこれの話。( this
とかには触れない)
カリー化
さて、結論から言うとこれは カリー化 と呼ばれるもので、先述した関数定義は次の構文のシンタックスシュガーみたいなものである。カリー化についてはいろいろ記事があると思うので 部分適応 や カリー化 で検索してもらえたらと思う。
const a = function (x) { return function (y) { console.log(x * y) } }
これは見たとおり、定義した a
は引数 x
を受け取って新しい関数を生成する関数だ。ここで生成される関数は最初に渡される x
を保持しているので、 function (y)
の中でも x
を利用できる。
実際に 2
を渡して実行してみても、関数が生成されていることが分かる。この関数はもちろん 2
を保持している。
$ node > const a = (x) => (y) => { ... console.log(x * y) ... } undefined > a (2) [Function]
生成された関数は引数 y
を受け取って console.log
するだけのものだ。計算に使う x
は最初の a (2)
で渡したものが引き継がれている。
次の2つは呼び出し方は違うが、結果は同じものである。これは生成された関数に 3
を渡してそのまま実行するパターン。
> a (2)(3) 6
そしてこれが、関数を一旦変数にいれてから 3
を渡して実行するパターン。そのまま実行するパターンではカリー化するメリットが無いので、ふつうはこうやって使う。
> const b = a(2) [Function] > b (3) 6
ちなみに関数定義ではいくらでも引数の定義をアローで繋げることができて、つなげた分だけ階層を深くすることができる。好きなだけ繋げるべし。
> const c = (x) => (y) => (z) => { ... console.log (x + y + z) ... } undefined > c (1)(2)(3) 6
使い方
この構文を使うタイミングについて少し言述する。
カリー化とはつまり、新たな関数を生み出して処理の共通化を行うためのアプローチだ。関数を生成する際に様々な設定値などを渡すことで、最適な新たな関数を生み出すことができる。この結果、何度も呼び出すが決め打ちの引数が入る煩わしい関数のをスマートに定義することができるようになる。
ありがちだが、例えば次のプログラムは消費税を計算するものだ。ここでは calcJPTax
という日本の消費税を計算する新しい関数を生成している。この関数を生成する際に渡す数値を変えて複数定義すれば、国に対応した消費税計算関数を簡単に作成することができる。
const product = (tax) => (value) => { return (value * tax) } const calcJPTax = product (1.08) console.log(calcJPTax(100)) //=> 108
少し難しいが、カリー化する際に関数を渡すこともできる。これを使うことで好きな処理を関数に差し込むことができる。
これはコンソールに値を出力する関数で、最初に渡す関数の返り値によって内容を変更できる。
const say = (func) => (value) => { return console.log(func(value)) } const sayHello = say ((name) => { return 'Hello, ' + name }) sayHello('Mario') //=> 'Hello, Mario'
などなど。様々な場面で使いみちがある。