約束の地

キャロ組

JavaScript のコールバック関数にて引数はどのように定義されるのか

コールバック関数の引数

JavaScript のコールバック関数の引数について、これまで得たいの知れない気持ち悪さのようなものがありました。率直に言うと「なぜこれで動くんだろう*1」という気持ちです。

あらためてコールバック関数の仕組みについて復習しました。

例とする記事

Qiita でほぼ説明し尽くされている記事がありますので、まずそれを示します。

この記事の中に「知ってないと感じる気持ち悪さ」という項目があり、まさにそこに書かれている内容が私の中で引っかかっていたことでした。

何が引っかかっていたのか

前述の記事の中での具体的なコードを見てみます。

function asyncFunc(input, callback) {
  setTimeout(function() {
    var data = getData(input);
    var err = data.getError();
    callback(err, data);
  }, 0);
}

asyncFunc('/pass/to/file', function(err, data) {
  if (err) throw err;
  console.log(data);
});

さらに、上記のコードの中でどこに引っかかっているかというと、以下の二点です。

  1. function asyncFunc(input, callback) と定義している時点ではコールバック関数の引数の個数がわからないこと
    • しかし、内部では callback(err, data) と呼ばれているので、コールバック関数の引数の個数は二つは指定しないといけない*2
  2. asyncFunc の第二引数のコールバック関数の引数*3の名称が予約語っぽい
    • 実際には予約語ではなく、errdata という引数名は自由に命名できる
    • しかし、asyncFunc 内部では callback(err, data) と呼ばれているため、それに合わせて errdata と命名するのは自然
    • 「実際には予約語でないけど実質予約語である」という状況が混乱を招く

つまり、以下の二点については、その内容を把握していないと正確なコールバック関数は書けないということになります。

  1. コールバック関数が取る引数の個数
  2. コールバック関数が取る引数がコールバック関数内で果たす役割*4

上記の二点が必須で、さらに以下の点が実質的に必須ということです。

  • 引数として与えるコールバック関数の引数*5は、呼ばれる関数の側でその役割が既に決まっているので、引数名の命名はその役割に沿ったもの*6を使う

補足

上記はコールバック関数に無名関数を与えた場合のお話になります。

結論

一度理解してしまえば難しくはないと思うんですが、ここの点は(も)やはり JavaScript のややこしいところだと思います。

*1:そして動くからいいや、というとてもダメな発想

*2:ということが callback の呼ばれ方の場所までコードを読んで初めて分かる

*3:err と data

*4:引数は何を引き連れてくるかということ

*5:ややこしい

*6:呼ばれる関数内での命名と同じもの

Powered by はてなブログ