web-dev-qa-db-fra.com

Dois-je placer des fonctions qui ne sont utilisées que dans une autre fonction, au sein de cette fonction?

Plus précisément, j'écris en JavaScript.

Disons que ma fonction principale est la fonction A. Si la fonction A effectue plusieurs appels à la fonction B, mais que la fonction B n'est utilisée nulle part ailleurs, dois-je simplement placer la fonction B dans la fonction A?

Est-ce une bonne pratique? Ou devrais-je toujours mettre la fonction B dans la même portée que la fonction A?

34
Gary

Je suis généralement en faveur des fonctions imbriquées, en particulier en JavaScript.

  • En JavaScript, la seule façon de limiter la visibilité d'une fonction est de l'imbriquer dans une autre fonction.
  • La fonction d'assistance est un détail d'implémentation privé. Le placer dans la même portée revient à rendre publiques les fonctions privées d'une classe.
  • S'il s'avère être d'une utilisation plus générale, il est facile de sortir, car vous pouvez être sûr que l'aide n'est actuellement utilisée que par cette seule fonction. En d'autres termes, cela rend votre code plus cohérent.
  • Vous pouvez souvent éliminer les paramètres, ce qui rend la signature et l'implémentation de la fonction moins verbeuses. Ce n'est pas la même chose que de rendre les variables globales. Cela ressemble plus à l'utilisation d'un membre de classe dans une méthode privée.
  • Vous pouvez donner aux fonctions d'assistance de meilleurs noms, plus simples, sans vous soucier des conflits.
  • La fonction d'aide est plus facile à trouver. Oui, il existe des outils qui peuvent vous aider. Il est toujours plus facile de déplacer un peu vos yeux vers l'écran.
  • Le code forme naturellement une sorte d'arbre d'abstraction: quelques fonctions générales à la racine, se ramifiant en plusieurs détails d'implémentation. Si vous utilisez un éditeur avec fonction repliée/réduite, l'imbrication crée une hiérarchie de fonctions étroitement liées au même niveau d'abstraction. Cela rend très facile d'étudier le code au niveau dont vous avez besoin et de masquer les détails.

Je pense que beaucoup d'opposition vient du fait que la plupart des programmeurs ont été élevés dans une tradition C/C++/Java, ou ont été enseignés par quelqu'un d'autre qui l'était. Les fonctions imbriquées ne semblent pas naturelles, car nous n'y étions pas beaucoup exposés lorsque nous apprenions à programmer. Cela ne signifie pas qu'ils ne sont pas utiles.

34
Karl Bielefeldt

Vous devez le mettre à l'échelle mondiale, pour plusieurs raisons.

  • L'imbrication d'une fonction d'assistance dans l'appelant augmente la longueur de l'appelant. La durée de la fonction est presque toujours un indicateur négatif; les fonctions courtes sont plus faciles à comprendre, à mémoriser, à déboguer et à maintenir.

  • Si la fonction d'assistance a un nom sensible, il suffit de lire ce nom sans avoir besoin de voir la définition à proximité. Si vous faites avez besoin de voir la définition d'aide pour comprendre la fonction de l'appelant, alors cet appelant en fait trop ou travaille sur trop de niveaux d'abstraction simultanément.

  • La disponibilité de l'assistant dans le monde permet à d'autres fonctions de l'appeler s'il s'avère qu'il est généralement utile après tout. Si l'assistant n'est pas disponible, vous êtes tenté de le copier-coller, ou de l'oublier et de le réimplémenter, mal, ou de rendre une autre fonction plus longue qu'elle ne doit l'être.

  • L'imbrication de la fonction d'assistance augmente la tentation d'utiliser des variables de la portée de l'appelant sans déclaration, de sorte qu'il devient difficile de savoir quelles sont les entrées et les sorties de l'aide. Si une fonction n'indique pas clairement sur quelles données elle opère et quels effets elle a, c'est généralement un signe de responsabilités peu claires. Déclarer l'assistant d'une fonction autonome vous oblige à savoir exactement ce qu'il fait réellement.

Modifier

Cela s'est avéré être une question plus controversée que je ne le pensais. Clarifier:

En JavaScript, les fonctions étendues de fichiers volumineux remplissent souvent le rôle des classes car le langage ne fournit aucun autre mécanisme limitant la portée. Certes, les fonctions d'aide devraient aller à l'intérieur de telles quasi-classes, pas en dehors.

Et le point sur la réutilisation plus facile présuppose que si un sous-programme le fait devient plus largement utilisé, vous êtes prêt à le déplacer complètement et à le mettre à la place appropriée, par exemple une bibliothèque d'utilitaires de chaîne ou dans votre registre de configuration global. Si vous ne voulez pas ordonner votre code comme ça, alors vous pourriez aussi bien imbriquer le sous-programme, comme vous le feriez avec un Method Object normal dans un langage plus "blocky".

14
Kilian Foth

Je vais proposer une troisième voie, pour placer les deux fonctions dans une fermeture. Cela ressemblerait à:

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

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

    return functionA;
})();

Nous créons la fermeture en enveloppant la déclaration des deux fonctions dans un IIFE . La valeur de retour de l'IIFE est la fonction publique, stockée dans une variable du nom de la fonction. La fonction publique peut être invoquée exactement de la même manière que si elle était déclarée en tant que fonction globale, c'est-à-dire functionA(). Notez que la valeur de retour est la fonction, pas un appel à la fonction, donc pas de parens à la fin.

En encapsulant les deux fonctions de cette façon, functionB est maintenant complètement privé et n'est pas accessible en dehors de la fermeture, mais n'est visible que par functionA. Il n'encombre pas l'espace de noms global, et n'encombre pas la définition de functionA.

8
cbojar

La définition de la fonction B dans la fonction A donne accès à la fonction B aux variables internes de A.

Ainsi, une fonction B définie dans une fonction A donne l'impression (ou du moins la possibilité) que B utilise ou modifie les variables locales de A. Tout en définissant B en dehors de A, il est clair que B ne le fait pas.

Par conséquent, pour la clarté du code, je définirais B dans A uniquement si B a besoin d'accéder aux variables locales de A. Si B a un objectif clair qui est indépendant de A, je le définirais certainement en dehors de A.

0
Florian F