具体的には、JavaScriptで記述しています。
関数Aが関数Aであるとしましょう。関数Aが関数Bを複数回呼び出しても、関数Bが他の場所で使用されていない場合は、関数Bを関数A内に配置する必要がありますか?
それは良い習慣ですか?または、関数Bを関数Aと同じスコープに配置する必要がありますか?
私は一般的に、特にJavaScriptにおいて、ネストされた関数を支持しています。
多くの反対は、ほとんどのプログラマーがC/C++/Javaの伝統に育てられたか、そうであった誰かから教えられたという事実から来ていると思います。ネストされた関数は、プログラムを学んでいるときにあまり触れられなかったため、自然に見えません。それはそれらが役に立たないという意味ではありません。
いくつかの理由で、グローバルスコープに配置する必要があります。
ヘルパー関数を呼び出し元にネストすると、呼び出し元の長さが長くなります。関数の長さはほとんどの場合負の指標です。短い関数は、理解、記憶、デバッグ、および保守が容易です。
ヘルパー関数にわかりやすい名前が付いている場合は、近くの定義を見なくても、その名前を読み取るだけで十分です。 do呼び出し元の関数を理解するためにヘルパー定義を確認する必要がある場合、その呼び出し元は多くの作業を行っているか、あまりに多くの抽象化レベルで同時に作業しています。
ヘルパーをグローバルに使用可能にすると、他の関数がヘルパーを呼び出して、結局それが一般的に役立つようになった場合にそれを呼び出すことができます。ヘルパーが利用できない場合、それをカットアンドペーストしたり、忘れて再実装したり、不十分にしたり、必要以上に別の関数を作成したりする誘惑に駆られます。
ヘルパー関数をネストすると、宣言なしで呼び出し元のスコープの変数を使用する誘惑が高まるため、ヘルパーの入力と出力が不明確になります。関数がどのデータに作用し、どのような影響を与えるかが明確に示されていない場合、それは通常、責任が不明確であることを示しています。ヘルパーのお尻をスタンドアロン関数として宣言すると、実際に何が行われるかを知る必要があります。
編集
それは私が思っていたよりも物議を醸す質問であることが判明しました。明確にするために:
JavaScriptでは、言語が他のスコープ制限メカニズムを提供しないため、大きなファイルスパニング関数がクラスの役割を果たすことがよくあります。確かにヘルパー関数はinsideのような疑似クラスであり、それらの外部ではありません。
そして、再利用のしやすさに関するポイントは、サブルーチンdoesがより広く使用されるようになった場合、それを完全に外して適切な場所に配置することをいとわないことを前提としています。文字列ユーティリティライブラリまたはグローバル構成レジストリ。このようにコードを順序付けしたくない場合は、通常の Method Object をより「ブロック状」の言語で行うのと同じように、サブルーチンをネストすることもできます。
クロージャー内に両方の関数を配置するための3番目のパスを提案します。それは次のようになります:
_var functionA = (function(){
function functionB() {
// do stuff...
}
function functionA() {
// do stuff...
functionB();
// do stuff...
}
return functionA;
})();
_
両方の関数の宣言を [〜#〜] iife [〜#〜] でラップして、クロージャーを作成します。 IIFEの戻り値はパブリック関数であり、関数の名前の変数に格納されます。 public関数は、functionA()
のように、グローバル関数として宣言された場合とまったく同じ方法で呼び出すことができます。戻り値は関数であり、関数の呼び出しではないため、最後に括弧がないことに注意してください。
このように2つの関数をラップすることで、functionB
は完全にプライベートになり、クロージャーの外からはアクセスできなくなりますが、functionA
にのみ表示されます。 、はfunctionA
の定義を乱雑にしていません。
関数A内で関数Bを定義すると、関数BはAの内部変数にアクセスできます。
したがって、関数A内で定義された関数Bは、BがAのローカル変数を使用または変更しているという印象(または少なくとも可能性)を与えます。 Aの外でBを定義すると、Bはそうではないことが明確になります。
したがって、コードをわかりやすくするために、BがAのローカル変数にアクセスする必要がある場合にのみ、A内にBを定義します。