Por que passar grandes funções anônimas como argumentos para outras funções tão amplamente aceitas no JavaScript?

27

Tenho uma opinião (que tenho certeza de que será compartilhada por alguns) de que a passagem de funções anônimas que contêm mais do que algumas linhas de código, como argumentos para outras funções, afeta drasticamente a legibilidade e a auto-documentação, a ponto de achar que seria será muito melhor para qualquer um que provavelmente use o código apenas para declarar uma função nomeada. Ou pelo menos atribua essa função anônima a uma variável antes de declarar a função principal

No entanto, muitas bibliotecas JavaScript (jQuery, d3.js / NVD3.js) apenas para dar alguns exemplos, usam grandes funções dessa maneira.

Por que isso é tão amplamente aceito em JavaScript? É algo cultural, ou há vantagens que estou perdendo, que tornariam o uso mais preferido do que declarar uma função nomeada?

Adam Copley
fonte
1
Provavelmente tem muito a ver com o uso de fechamentos . Também pode ter a ver com não querer expor a função real ao mundo exterior (e é por isso que é anônimo).
Robert Harvey
3
@RobertHarvey Em outras palavras, é uma solução alternativa para o JavaScript não ter público e privado ?
Mason Wheeler
2
Em muitos lugares, uma grande função anônima parece mais um bloco e geralmente se sente muito bem quando você se acostuma. As regras de escopo até suportam a sensação de bloqueio.
5
@MasonWheeler: Isso depende da sua perspectiva. Um programador de Scheme ou ECMAScript pode dizer isso publice privateé uma solução alternativa por não ter um fechamento adequado.
Jörg W Mittag 27/02
1
@ JörgWMittag Hooray for Racket, o patrocinador oficial do XKCD 927!
Mason Wheeler

Respostas:

23

Três razões principais em que posso pensar:

  1. Acesso ao escopo pai
  2. Privacidade
  3. Redução de nomes definidos em escopos superiores

Acesso ao escopo pai: as definições de função embutida permitem que o código embutido tenha acesso às variáveis ​​definidas nos escopos pai. Isso pode ser muito útil para muitas coisas e pode reduzir a quantidade ou a complexidade do código, se feito corretamente.

Se você colocar o código em uma função definida fora deste escopo e depois chamar o código, precisará passar qualquer estado pai que ele deseje acessar para a função.

Privacidade: o código dentro de uma definição anônima embutida é mais privado e não pode ser chamado por outro código.

Redução de nomes definidos em escopos mais altos: isso é mais importante ao operar no escopo global, mas uma declaração anônima em linha impede que seja necessário definir um novo símbolo no escopo atual. Como o Javascript não exige nativamente o uso de espaços para nome, é aconselhável evitar definir mais símbolos globais do que o mínimo necessário.


Editorial: Parece ter se tornado algo cultural em Javascript, onde declarar algo anonimamente inline é considerado "melhor" do que definir uma função e chamá-la mesmo quando o acesso ao escopo pai não é usado. Suspeito que isso tenha ocorrido inicialmente devido ao problema global de poluição do namespace em Javascript e, talvez, devido a problemas de privacidade. Mas agora se transformou em algo cultural, e você pode vê-lo expresso em muitos órgãos públicos de código (como os mencionados).

Em linguagens como C ++, a maioria provavelmente consideraria uma prática abaixo do ideal ter uma função gigante que se estende por várias páginas / telas. Obviamente, o C ++ possui espaço para nome embutido, não fornece acesso ao escopo pai e possui recursos de privacidade, para que possa ser totalmente motivado pela legibilidade / manutenção, enquanto o Javascript deve usar a expressão de código para obter privacidade e acesso ao escopo pai. Assim, JS parece ter sido motivado em uma direção diferente e tornou-se algo cultural na linguagem, mesmo quando as coisas que motivaram essa direção não são necessárias em um caso específico.

jfriend00
fonte
Como um desenvolvedor de C ++ recentemente se mudou para JS para grande parte do meu trabalho, essa resposta faz muito sentido, particularmente o ponto de 'acesso ao escopo pai' - ele realmente tem o poder de simplificar bastante o seu código. Um nitpick - C ++ fornece acesso ao escopo pai no lambda C ++ 11 :) Definitivamente +1.
Comandante Coentro Salamandra,
8

Funções anônimas são usadas para muito mais propósitos em JavaScript do que na maioria dos idiomas.

Primeiro, eles são usados ​​para espaçamento de nomes e escopo de bloco. Até recentemente, o JavaScript não possuía módulos ou qualquer outro tipo de mecanismo de namespacing, o que levou ao uso de funções anônimas para fornecer essa funcionalidade por meio do padrão de módulo. Não haveria absolutamente nenhum benefício em nomear essas funções. Em uma escala menor, devido à falta de escopo do bloco do JavaScript até recentemente, um padrão semelhante foi usado para imitar o escopo do bloco; mais notavelmente no corpo de loops. Usar uma função nomeada nesse caso seria ofuscantemente ativa.

Segundo, e menos específicas do JavaScript, as funções anônimas são frequentemente usadas com funções de ordem superior que imitam as estruturas de controle. Por exemplo, o eachmétodo do jQuery . Duvido que você abstraia cada corpo de loop ou se-ramifique em uma função sempre que tiver mais do que algumas linhas. A mesma lógica se aplica neste caso.

Uma razão final é que a programação baseada em eventos é comum no JavaScript, o que tende a levar a um código de estilo de passagem de continuação inadvertido . Você faz uma chamada AJAX e registra um retorno de chamada que, quando executado, fará outra chamada AJAX e registra um retorno de chamada etc. Se as chamadas forem síncronas e não assíncronas, isso seria apenas uma sequência linear de código em um único escopo lexical . Novamente, duvido que você abstraia todas as poucas linhas de código linear em uma função.

Também existem fatores culturais e, pelas razões acima, entre outras, funções anônimas são muito mais comuns em JavaScript do que em muitos outros idiomas e são usadas mais confortavelmente / menos do que em muitos outros idiomas.

Derek Elkins
fonte
Minhas próprias estruturas, talvez ingênuas na codificação C ++, às vezes adotaram princípios de codificação baseada em eventos e retornos de chamada do JavaScript via std :: function e lambdas. No meu caso, é parcialmente porque estou fazendo o código da interface do usuário em C ++ e não quero executar operações de bloqueio. Estou curioso para saber se as práticas de JavaScript são úteis para qualquer pessoa, desde que a linguagem as suporte bem.
Katana314