Devo colocar funções que são usadas apenas em outra função dentro dessa função?

30

Especificamente, estou escrevendo em JavaScript.

Digamos que minha função principal seja a Função A. Se a Função A fizer várias chamadas para a Função B, mas a Função B não for usada em nenhum outro lugar, devo colocar a Função B na Função A?

Essa é uma boa prática? Ou ainda devo colocar a Função B no mesmo escopo da Função A?

Gary
fonte

Respostas:

32

Geralmente sou a favor de funções aninhadas, especialmente em JavaScript.

  • No JavaScript, a única maneira de limitar a visibilidade de uma função é aninhando-a dentro de outra função.
  • A função auxiliar é um detalhe de implementação privada. Colocá-lo no mesmo escopo é semelhante a tornar públicas as funções privadas de uma classe.
  • Se for de uso mais geral, é fácil sair, porque você pode ter certeza de que o auxiliar atualmente é usado apenas por essa função. Em outras palavras, isso torna seu código mais coeso.
  • Muitas vezes, você pode eliminar parâmetros, o que torna a assinatura e a implementação da função menos detalhadas. Isso não é o mesmo que tornar variáveis ​​globais. É mais como usar um membro da classe em um método privado.
  • Você pode dar às funções auxiliares nomes melhores e mais simples, sem se preocupar com conflitos.
  • A função auxiliar é mais fácil de encontrar. Sim, existem ferramentas que podem ajudá-lo. Ainda é mais fácil mover os olhos um pouco para cima na tela.
  • O código naturalmente forma uma espécie de árvore de abstração: algumas funções gerais na raiz, ramificando-se em vários detalhes de implementação. Se você usar um editor com a função dobrando / recolhendo, o aninhamento criará uma hierarquia de funções intimamente relacionadas no mesmo nível de abstração. Isso facilita muito o estudo do código no nível necessário e oculta os detalhes.

Eu acho que muita oposição vem do fato de que a maioria dos programadores foi criada em uma tradição C / C ++ / Java ou foi ensinada por outra pessoa que era. Funções aninhadas não parecem naturais porque não fomos expostas a elas muito quando estávamos aprendendo a programar. Isso não significa que eles não são úteis.

Karl Bielefeldt
fonte
14

Você deve colocá-lo no escopo global, por vários motivos.

  • Aninhar uma função auxiliar no chamador aumenta a duração do chamador. O comprimento da função é quase sempre um indicador negativo; funções curtas são mais fáceis de entender, memorizar, depurar e manter.

  • Se a função auxiliar tiver um nome sensato, basta ler esse nome sem precisar ver a definição nas proximidades. Se você fazer necessidade de ver a definição auxiliar, a fim de compreender a função de chamadas, então essa pessoa está fazendo muito, ou está trabalhando em muitos níveis de abstração simultaneamente.

  • Ter o ajudante disponível globalmente permite que outras funções o chamem, se assim o tornar útil em geral. Se o auxiliar não estiver disponível, você será tentado a cortá-lo e colá-lo, ou esquecê-lo e reimplementá-lo mal ou fazer outra função mais longa do que precisa.

  • Aninhar a função auxiliar aumenta a tentação de usar variáveis ​​do escopo do chamador sem declaração, para que fique claro quais são as entradas e saídas do auxiliar. Se uma função não indica claramente em que dados ela opera e quais efeitos ela tem, geralmente é um sinal de responsabilidades pouco claras. Declarar o auxiliar como uma função independente força você a saber exatamente o que ele realmente faz.

Editar

Essa acabou sendo uma questão mais controversa do que eu pensava. Esclarecer:

Em JavaScript, grandes funções de abrangência de arquivos geralmente cumprem o papel de classes porque a linguagem não fornece nenhum outro mecanismo de limitação de escopo. Certamente funções auxiliares deve ir dentro desses quase-aulas, não fora delas.

E a questão da reutilização mais fácil pressupõe que, se uma sub-rotina se tornar mais amplamente usada, você estará disposto a movê-la completamente e colocá-la no lugar apropriado, por exemplo, uma biblioteca de utilitários de string ou em seu registro de configuração global. Se você não deseja ordenar seu código dessa maneira, é melhor aninhar a sub-rotina, como faria com um Objeto de Método normal em uma linguagem mais "em bloco".

Kilian Foth
fonte
Obrigado, todos os pontos muito bons. Em uma nota lateral, como geralmente devo ordenar minhas funções? Por exemplo, no momento, com pequenos programas, eu apenas os ordeno em ordem alfabética. Mas devo agrupar as funções de auxiliar e de quem chama? Eu acho que eu deveria pelo menos quebrar minhas funções com base em quais partes do aplicativo elas se aplicam, por exemplo?
Gary
Não me incomodo em pedir definições de método há muito tempo. Se você programa com algum grau de profissionalismo, deve ter um ambiente que permita acessar qualquer definição com algumas teclas pressionadas. Portanto, métodos curtos são úteis, mas arquivos de classe curtos ou mesmo bem ordenados agregam pouco valor. (Claro, JavaScript usado para exigir que todas as definições para ser colocado acima de seus usos ou o resultado seria tecnicamente um comportamento indefinido -. Não me lembro se esse ainda é o caso ou não)
Kilian Foth
2
Esta é uma ótima resposta. O problema do encapsulamento quebrado é algo que muitos não consideram ao usar funções internas.
Sbichenko
2
Globais são um cheiro de código. Eles causam acoplamentos desnecessários que impedem a refatoração, causam conflitos de nomes, expõem detalhes de implementação etc. Isso é especialmente ruim em Javascript, pois todas as variáveis ​​são mutáveis, o código normalmente é carregado diretamente de terceiros, ainda não há (ainda) um sistema de módulos amplamente disponível, ou mesmo uma implementação amplamente disponível de "let". Em um ambiente tão hostil, a única estratégia defensiva que temos é o encapsulamento dentro de funções únicas .
Warbo 12/08/14
11
"Cumprir o papel das classes" está tentando escrever JS como se fosse outra coisa. Qual é o "papel das classes"? Verificação de tipo? Modularidade? Compilação encenada? Herança? A questão envolve o escopo, então suponho que você queira dizer as regras grosseiras do escopo das classes. Isso está apenas passando a bola: como as classes devem ter um escopo? O PHP os torna globais, o que não fornece encapsulamento. O Python define classes lexicamente, mas se você estiver em um bloco com escopo lexicamente, por que agrupar tudo em outro bloco com escopo de classe? O JS não possui essa redundância: basta usar o escopo lexical necessário.
Warbo 13/08/14
8

Vou propor um terceiro caminho, para colocar as duas funções em um fechamento. Seria como:

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

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

    return functionA;
})();

Criamos o fechamento envolvendo a declaração de ambas as funções em um IIFE . O valor de retorno do IIFE é a função pública, armazenada em uma variável do nome da função. A função pública pode ser chamada exatamente da mesma maneira como se fosse declarada como uma função global, ie functionA(). Observe que o valor de retorno é a função , não uma chamada para a função, portanto, não há parênteses no final.

Ao agrupar as duas funções dessa maneira, functionBagora é completamente privado e não pode ser acessado fora do fechamento, mas é visível apenas para functionA. Não está bagunçando o espaço para nome global e não está bagunçando a definição de functionA.

cbojar
fonte
0

Definir a função B na função A dá à função B acesso às variáveis ​​internas de A.

Portanto, uma função B definida dentro de uma função A dá a impressão (ou pelo menos a possibilidade) de que B esteja usando ou alterando as variáveis ​​locais de A. Ao definir B fora de A, fica claro que B não.

Portanto, para maior clareza do código, eu definiria B dentro de A apenas se B precisar acessar variáveis ​​locais de A. Se B tiver um objetivo claro e independente de A, eu definitivamente o definiria fora de A.

Florian F
fonte
-1

Você não deve aninhar, pois, caso contrário, a função aninhada será recriada toda vez que você chamar a função externa.

Domenic
fonte
-3

Como a função B não é usada em nenhum outro lugar, você também pode transformá-la em uma função interna na função A, porque essa é a única razão pela qual ela existe.

Jack_9
fonte
11
este não parece acrescentar nada substancial sobre pontos feitos e explicado em anteriores 3 respostas
mosquito