他の1つの関数でのみ使用される関数をその関数内に配置する必要がありますか?


30

具体的には、JavaScriptで記述しています。

私の主な機能が機能Aであるとしましょう。機能Aが機能Bを複数回呼び出し、機能Bが他のどこでも使用されていない場合、機能A内に機能Bを配置するだけですか?

それは良い習慣ですか?または、関数Bを関数Aと同じスコープに配置する必要がありますか?

回答:


32

私は一般的にネストされた関数、特にJavaScriptを好んでいます。

  • JavaScriptでは、関数の可視性を制限する唯一の方法は、関数を別の関数内にネストすることです。
  • ヘルパー関数は、プライベートな実装の詳細です。同じスコープに置くことは、クラスのプライベート関数をパブリックにすることに似ています。
  • それがより一般的な使用であることが判明した場合、ヘルパーは現在その1つの関数によってのみ使用されていると確信できるため、簡単に移動できます。言い換えれば、コードをよりまとまりのあるものにします。
  • 多くの場合、パラメーターを削除できます。これにより、関数のシグネチャと実装の冗長性が低くなります。これは、変数をグローバルにすることとは異なります。これは、プライベートメソッドでクラスメンバーを使用するようなものです。
  • 競合を心配することなく、ヘルパー関数をより良く、より単純な名前にすることができます。
  • ヘルパー関数は見つけやすいです。はい、あなたを助けることができるツールがあります。画面上で目を少し動かすだけの方が簡単です。
  • コードは当然、抽象化の一種のツリーを形成します。ルートにあるいくつかの一般的な関数は、いくつかの実装の詳細に分岐します。関数の折りたたみ/折りたたみでエディターを使用する場合、ネストは同じ抽象化レベルで密接に関連する関数の階層を作成します。これにより、必要なレベルでコードを簡単に学習し、詳細を隠すことができます。

多くの反対派は、ほとんどのプログラマーがC / C ++ / Javaの伝統で育てられたか、他の誰かに教えられたという事実から来ていると思います。入れ子になった関数は、プログラミングを学んでいたときにあまり触れられなかったため、自然に見えません。それは、それらが役に立たないという意味ではありません。


14

いくつかの理由により、グローバルスコープに配置する必要があります。

  • ヘルパー関数を呼び出し元にネストすると、呼び出し元の長さが長くなります。関数の長さはほとんどの場合負の指標です。短い関数は理解しやすく、記憶しやすく、デバッグや保守が簡単です。

  • ヘルパー関数に意味のある名前がある場合は、近くの定義を見る必要なく、その名前を読むだけで十分です。あなたがいる場合行う、呼び出し元の機能を理解するために、ヘルパー定義を参照する必要性を、その呼び出し側はあまりやっている、または同時に抽象化のあまりに多くのレベルに取り組んでいます。

  • ヘルパーをグローバルに使用可能にすると、他の関数が一般的に有用になった場合にそれを呼び出すことができます。ヘルパーが利用できない場合、それをカットアンドペーストしたり、それを忘れて再実装したり、他の機能を必要以上に長くしたいと思うでしょう。

  • ヘルパー関数をネストすると、宣言せずに呼び出し元のスコープの変数を使用する誘惑が高まり、ヘルパーの入力と出力が不明になります。関数がどのデータをどのように処理し、どのような影響を与えるかを明確に述べていない場合、それは通常、責任が不明確であることを示しています。ヘルパー関数をスタンドアロン関数として宣言すると、実際に何をするのかを知る必要があります。

編集

それは、私が思っていたよりも議論の余地のある問題であることが判明しました。明確にするために:

JavaScriptでは、言語が他のスコープ制限メカニズムを提供しないため、大きなファイルスパン関数がクラスの役割を果たすことがよくあります。確かに、ヘルパー関数は、そのような準クラスの外側ではなく、内側にあるべきです。

また、再利用が簡単になるという点、サブルーチンより広く使用されるようになった場合、完全に適切な場所に移動して、適切な場所、たとえば文字列ユーティリティライブラリまたはグローバル構成レジストリに配置することを前提としてます。そのようなコードを注文したくない場合は、より「ブロックの多い」言語で通常のメソッドオブジェクトを使用するのと同じように、サブルーチンをネストすることもできます。


ありがとう、すべて非常に良い点。副次的に、どのように機能を注文するのが一般的ですか?たとえば、現時点では、小さなプログラムではアルファベット順に並べています。しかし、ヘルパー関数と呼び出し元関数をグループ化する必要がありますか?たとえば、アプリケーションのどの部分に適用するかに基づいて、少なくとも機能を分解する必要があると思いますか?
ゲイリー14

私は長い間、メソッド定義の順序付けを気にしませんでした。ある程度のプロ意識を持ってプログラミングする場合は、とにかく数回のキーストロークで任意の定義にジャンプできる環境が必要です。したがって、短いメソッドは便利ですが、短いクラスファイルや適切に並べられたクラスファイルでもほとんど価値がありません。(もちろん、JavaScriptはその用途の上に配置されるすべての定義を必要とするために使用されるか、または結果が技術的に未定義の動作になります- 。それは今でもそうであるかどう思い出すことができない)
キリアンFoth

2
これは素晴らしい答えです。破損したカプセル化の問題は、内部関数を使用するときに多くの人が考慮しないものです。
sbichenko

2
グローバルはコードの匂いです。これらは、リファクタリングを妨げる不必要なカップリングを引き起こし、名前の競合を引き起こし、実装の詳細などを公開します。これは、すべての変数が可変であり、コードは通常サードパーティから直接ロードされるため、Javascriptでは特に悪いです広く利用可能なモジュールシステム、または「let」の広く利用可能な実装です。このような敵対的な環境では、私たちが持っている唯一の防御戦略は、ワンショット機能内へのカプセル化です。
ウォーボ14

1
「クラスの役割を果たす」ことは、あたかも何か別のものであるかのようにJSを書こうとしています。「クラスの役割」とは何ですか?型チェック?モジュール性?段階的なコンパイル?継承?質問にはスコーピングが含まれるので、クラスの粗いスコーピングルールを意味すると思います。それはちょうどお金を払っています:クラスはどのようにスコープされるべきですか?PHPはそれらをグローバルにし、カプセル化を提供しません。Pythonはクラスをレキシカルにスコープしますが、レキシカルスコープのブロックにいる場合、なぜすべてを別のクラススコープのブロックでラップするのですか?JSにはそのような冗長性はありません。必要なだけレキシカルスコープを使用してください。
ウォーボ

8

クロージャー内に両方の機能を配置するために、3番目のパスを提案します。次のようになります。

var functionA = (function(){
    function functionB() {
        // do stuff...
    }

    function functionA() {
        // do stuff...
        functionB();
        // do stuff...
    }

    return functionA;
})();

IIFEで両方の関数の宣言をラップすることにより、クロージャーを作成します。IIFEの戻り値はパブリック関数であり、関数の名前の変数に格納されます。パブリック関数は、グローバル関数として宣言された場合とまったく同じ方法で呼び出すことができますfunctionA()。戻り値は関数の呼び出しではなく関数であるため、最後に括弧がないことに注意してください。

このように2つの関数をラップすることにより、functionB完全にプライベートになり、クロージャの外部からアクセスすることはできませんが、のみに表示されfunctionAます。これは、グローバルな名前空間を乱雑にされていない、の定義を乱雑にされていませんfunctionA


0

関数A内で関数Bを定義すると、関数BがAの内部変数にアクセスできます。

したがって、関数A内で定義された関数Bは、BがAのローカル変数を使用または変更しているという印象(または少なくとも可能性)を与えます。Aの外でBを定義すると、Bが定義しないことが明らかになります。

したがって、コードを明確にするために、BがAのローカル変数にアクセスする必要がある場合にのみ、A内でBを定義します。


-1

ネストしないでください。ネストしないと、外側の関数を呼び出すたびにネストされた関数が再作成されるためです。


-3

関数Bは他のどこでも使用されていないため、関数Aの内部機能とすることもできます。それが唯一の理由だからです。


1
これが行われ、前3つの答えで説明したもの大幅なオーバーポイントを追加していないようだ
ブヨ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.