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

コールバック関数の引数

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

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

例とする記事

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

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

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

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

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

asyncFunc('/path/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 と命名するのは自然
    • 「実際には予約語でないけど実質予約語である*4」という状況が混乱を招く

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

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

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

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

補足

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

結論

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

余談

技術書や技術記事を読んでいて困る場合は、私は以下の場合だと思います。

  • それまでに説明がない事柄が突然何の説明もなく現れて使われる場合
    • あることについて「分かっているもの」として話が進む場合など
  • 説明の前後関係に整合性がない場合
    • 例えば、「関数の引数を増やす」というテーマの説明があったときに、引数が一つのときの説明は function hoge(a)... というように a という仮引数を設定しているのですが、引数が二つに増えた場合の説明においては function hoge(x, y)... のように、a が消え去る場合
      • 私だけかもしれませんが、「なぜ ab を仮引数にせずに xy を使ったのか」ということを考え始めて進行がストップします*8
  • 同じ意味を説明するのに、異なる用語で説明している場合*9
  • 「予約語」がそうであると書かれていない、あるいはその逆で、どういう命名でもよい部分に「予約語」っぽい名前を命名している
  • 推敲不足

もっとも、私が書く文章では上の内容のオンパレードではあるので、自分でも気をつけなければいけないと常々思っています。

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

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

*3:err と data

*4:という表現はまた微妙に誤解を招きそうですが、イメージとしてそんな感じです

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

*6:ややこしい

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

*8:そしてそれはたいてい「なんとなく」が理由であったりします

*9:特別な意図がなくそのようにしている場合

Powered by はてなブログ