コールバック関数の引数
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); });
さらに、上記のコードの中でどこに引っかかっているかというと、以下の二点です。
function asyncFunc(input, callback)
と定義している時点ではコールバック関数の引数の個数がわからないこと- しかし、内部では
callback(err, data)
と呼ばれているので、コールバック関数の引数の個数は二つは指定しないといけないということがわかる*2
- しかし、内部では
asyncFunc()
の第二引数のコールバック関数の引数*3の名称が予約語っぽい- 実際には予約語ではなく、
err
とdata
という引数名は自由に命名できる - しかし、
asyncFunc
内部ではcallback(err, data)
と呼ばれているため、それに合わせてerr
とdata
と命名するのは自然 - 「実際には予約語でないけど実質予約語である*4」という状況が混乱を招く
- 実際には予約語ではなく、
つまり、以下の二点については、その内容を把握していないと正確なコールバック関数は書けないということになります。
- コールバック関数が取る引数の個数
- コールバック関数が取る引数がコールバック関数内で果たす役割*5
上記の二点が必須で、さらに以下の点が実質的に必須ということです。
補足
上記はコールバック関数に無名関数を与えた場合のお話になります。
結論
一度理解してしまえば難しくはないと思うんですが、ここの点は(も)やはり JavaScript のややこしいところだと思います。
余談
技術書や技術記事を読んでいて困る場合は、私は以下の場合だと思います。
- それまでに説明がない事柄が突然何の説明もなく現れて使われる場合
- あることについて「分かっているもの」として話が進む場合など
- 説明の前後関係に整合性がない場合
- 例えば、「関数の引数を増やす」というテーマの説明があったときに、引数が一つのときの説明は
function hoge(a)...
というようにa
という仮引数を設定しているのですが、引数が二つに増えた場合の説明においてはfunction hoge(x, y)...
のように、a
が消え去る場合- 私だけかもしれませんが、「なぜ
a
とb
を仮引数にせずにx
とy
を使ったのか」ということを考え始めて進行がストップします*8
- 私だけかもしれませんが、「なぜ
- 例えば、「関数の引数を増やす」というテーマの説明があったときに、引数が一つのときの説明は
- 同じ意味を説明するのに、異なる用語で説明している場合*9
- 「予約語」がそうであると書かれていない、あるいはその逆で、どういう命名でもよい部分に「予約語」っぽい名前を命名している
- 推敲不足
もっとも、私が書く文章では上の内容のオンパレードではあるので、自分でも気をつけなければいけないと常々思っています。