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?
Je suis généralement en faveur des fonctions imbriquées, en particulier en JavaScript.
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.
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".
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
.
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.