HOWTO · JavaScript
JavaScript の入れ子関数
このチュートリアルでは、JavaScript の入れ子関数について説明します。
このページの内容
入れ子関数とは、別の関数の中で定義された関数です。外側の関数によって内側の関数を利用できる範囲が決まり、内側の関数は定義されたスコープにある変数を読み取れます。このガイドでは、入れ子関数の定義と呼び出し方、外側の変数にアクセスできる利点、読みやすさを保つ方法を説明します。
入れ子関数とは?
JavaScriptでは、内側の関数から外側の関数の引数やローカル変数を使えます。外側の関数が戻った後も内側の関数を参照できる状態であれば、内側の関数は参照している変数へのアクセスを保ちます。関数と、その関数に保たれたレキシカルスコープへのアクセスを合わせたものがクロージャです。
次の例を実行してみましょう。
function makeAdder(amount) {
return function add(value) {
return amount + value;
};
}
const addFive = makeAdder(5);
console.log(addFive(10));
15
提示された実行結果は 15 です。makeAdder(5) は内側の関数を返し、その関数が 10 を引数にして呼び出されます。内側の関数は、外側の呼び出しのレキシカルスコープから amount を読み取ります。内側の関数を返すのは、クロージャを利用する一般的なパターンです。ただし、これだけでカリー化とは限りません。カリー化とは、複数の引数を受け取る関数を、それぞれ1つの引数を受け取る関数の列に変換することです。
入れ子関数を使う利点
入れ子にすると、補助関数を使うコードの近くに置けるため、周囲のスコープに名前を追加せずに済みます。返された内側の関数で、呼び出しをまたいで状態を保つこともできます。たとえば、カウンターを作る関数でローカル変数 count を用意し、それを更新する関数を返せます。カウンターを作る関数を呼び出すたびに別々の束縛が作られるため、複数のカウンターでそれぞれ独立した状態を保てます。この動作を実現するのがクロージャですが、実行が速くなるとは限りません。
内側の関数から外側の変数にアクセスできても、すべてのコールバックが入れ子関数になるわけではありません。別の場所で定義したコールバックを関数に渡した場合、それは高階関数に渡されたコールバックです。別の関数の中で定義された場合にのみ、入れ子関数と呼べます。
入れ子関数を使う場面
外側の関数の値やスコープが必要な場合、補助関数がその場所でのみ意味を持つ場合、または乗数のような値を設定した関数を作りたい場合に、入れ子関数を使うと便利です。設定済みの関数を返せば、呼び出すたびにその設定値を渡さずに再利用できます。
補助関数が外側の関数の変数やスコープを必要としないなら、より広いスコープで定義したほうが再利用やテストがしやすくなることがあります。単に関数を別の関数の中に置けるからではなく、関数同士の関係が明確になる場合や状態を保ちたい場合に、入れ子にしましょう。
入れ子関数のベストプラクティス
各関数の役割を絞り、外側の準備と内側の処理の関係が分かる名前を付けましょう。処理の流れを追いにくくするほど入れ子を重ねないでください。補助関数を単独で使える場合や、外側の関数の処理を見通しにくくしている場合は、外に切り出しましょう。
クロージャは、参照可能な状態にある間、レキシカル環境へのアクセスを保ちます。これは通常の動作であり、クロージャを一律に避ける理由にはなりません。長く存続するコードでは、大きなオブジェクトへの参照も保ってしまうクロージャを、必要以上に参照可能な状態にしないようにしましょう。
まとめ
入れ子関数は別の関数の中で定義され、レキシカルスコープを通じて外側の関数の変数を使えます。返された場合はクロージャとしてそのアクセスを保ち、プライベートな状態や再利用できる設定を実現できます。関数同士の関係がコードを分かりやすくする場面で使い、処理が見えにくくなる場合は入れ子を減らしましょう。
よくある質問
- JavaScriptの入れ子関数とは何ですか? 別の関数の中で定義され、その外側のレキシカルスコープにある変数へアクセスできる関数です。
- 入れ子関数を使う理由は何ですか? 補助関数をその場に限定したり、クロージャで状態を保ったり、外側の値を設定した関数を作ったりできます。必ず実行が速くなるわけではありません。
- 入れ子関数から外側の関数の変数にアクセスできますか? はい。内側の関数はレキシカルスコープにある変数を読み取れます。内側の関数を参照できる状態であれば、外側の関数が戻った後もアクセスできます。
- 入れ子関数の欠点は何ですか? 入れ子が深くなりすぎると、コードが読みづらく、保守しにくくなることがあります。
- 入れ子関数をうまく使うにはどうすればよいですか? 関数の役割を絞って分かりやすい名前を付け、外側のスコープが不要な補助関数はより広いスコープで定義しましょう。