Como começar rapidamente com VUE3.0: Insira
recomendações relacionadas ao aprendizado: Tutorial de aprendizado de JavaScript
Já vi muitas explicações sobre programação funcional, mas a maioria delas está no nível teórico e algumas são apenas para linguagens de programação funcionais puras como Haskell. O objetivo deste artigo é falar sobre a prática específica da programação funcional em JavaScript aos meus olhos. A razão pela qual está "aos meus olhos" significa que o que digo representa apenas minha opinião pessoal, que pode entrar em conflito com alguns conceitos estritos.
Este artigo omitirá muita introdução formal ao conceito e se concentrará em mostrar o que é código funcional em JavaScript, qual é a diferença entre código funcional e escrita geral, quais benefícios o código funcional pode nos trazer e quais são alguns modelos funcionais comuns?
Acho que a programação funcional pode ser entendida como um método de programação que usa funções como principal portador. O uso de funções para desmontar e abstrair expressões gerais
é comparado com imperativos. Os pontos principais são os seguintes:
semântica mais clara, maior capacidade de reutilização, maior capacidade de manutenção, melhor limitação de escopo e menos efeitos colaterais Programação funcional básica O exemplo a seguir é uma expressão funcional específica de
código Javascript
// cada palavra na matriz, coloque a letra maiúscula. primeira letra // Escrita geral const arr = ['apple', 'pen', 'apple-pen'] for(const i in arr){; const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1 }); console.log(arr); // Método de escrita funcional - function upperFirst(word) { retornar palavra[0].toUpperCase() + palavra.slice(1); função palavraToUpperCase(arr) { retornar arr.map(superiorPrimeiro); console.log(wordToUpperCase(['apple', 'pen', 'apple-pen'])); // Método de escrita funcional 2 console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1))) ;
Quando a situação se torna mais complexa, a forma de escrever expressões encontrará vários problemas:
o significado não é óbvio, torna-se gradualmente difícil de manter, a reutilização é fraca, mais código será gerado e muitas variáveis intermediárias serão geradas. A programação funcional resolveu bem os problemas acima. Primeiro, consulte o método de escrita funcional 1, que usa encapsulamento de função para decompor funções (a granularidade não é única), encapsula-as em funções diferentes e, em seguida, usa chamadas combinadas para atingir o objetivo. Isso torna a expressão clara e fácil de manter, reutilizar e ampliar. Em segundo lugar, usando funções de ordem superior, Array.map substitui for...of pela travessia do array, reduzindo variáveis e operações intermediárias.
A principal diferença entre o método de escrita funcional 1 e o método de escrita funcional 2 é que você pode considerar se a função pode ser reutilizada posteriormente. Caso contrário, o último é melhor.
A partir do método de escrita funcional 2 acima, podemos ver que no processo de escrita de código funcional é fácil causar extensão horizontal, ou seja, produzir múltiplas camadas de aninhamento. Vamos dar um exemplo extremo abaixo.
Código Javascript
// Calcula a soma dos números // Método geral de escrita console.log(1 + 2 + 3 - 4) // Escrita funcional function sum(a, b) { retornar a + b } função sub(a, b) { retornar a -b } console.log(sub(sum(sum(1, 2), 3), 4); Este exemplo mostra apenas um caso extremo de extensão horizontal. À medida que o número de níveis aninhados de funções continua a aumentar, o código se tornará menos legível . O desempenho é bastante reduzido e é fácil cometer erros. Neste caso, podemos considerar vários métodos de otimização, como a seguinte otimização em cadeia. // Otimiza a escrita (bem, você leu certo, esta é a escrita em cadeia do lodash) Código Javascript const utils = { cadeia(a) { isto._temp = a; devolva isso; }, soma(b) { isto._temp += b; devolva isso; }, sub(b) { isto._temp -= b; devolva isso; }, valor() { const _temp = this._temp; this._temp = indefinido; retornar _temp; } }; console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
Após reescrever desta forma, a estrutura geral ficará mais clara e cada elo da cadeia será O que fazer também pode ser mostrado facilmente. Outro bom exemplo de comparação entre aninhamento e encadeamento de funções é a função de retorno de chamada e o padrão Promise.
Código Javascript
// Solicita duas interfaces sequencialmente // Função de retorno de chamada import $ from 'jquery'; se(rs){ $.post('a/url/para/outro/destino', (rs2) => { se(rs2){ $.post('a/url/para/terceiro/destino'); } }); } }); // Promessa de solicitação de importação de 'catta'; // catta é uma ferramenta de solicitação leve que suporta fetch, jsonp, ajax e não possui dependências request('a/url/to/target') .then(rs => rs ? $.post('a/url/to/another/target'): Promise.reject()) .then(rs2 => rs2 ? $.post('a/url/to/third/target') : Promise.reject());
À medida que o nível de aninhamento da função de retorno de chamada e a complexidade de nível único aumentam, ela se tornará É. inchado e difícil de manter, mas a estrutura da cadeia do Promise ainda pode se expandir verticalmente quando a complexidade é alta e o isolamento hierárquico é muito claro.
O
pode reter variáveis locais em um bloco de código que não é liberado.
O conceito de fechamento é relativamente abstrato e usa esse recurso
. as malas podem nos trazer?
Vamos primeiro ver como criar um encerramento:
Código Javascript
// Criar uma função de encerramento makeCounter() { seja k = 0; função de retorno() { retornar++k; }; const contador = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter O bloco de código desta função faz referência à variável local k na função retornada, causando falha na variável local. função é executada, ela é reciclada pelo sistema, gerando assim um fechamento. A função deste encerramento é "reter" a variável local para que a variável possa ser reutilizada quando a função interna for chamada, ao contrário das variáveis globais, esta variável só pode ser referenciada dentro da função;
Em outras palavras, os encerramentos na verdade criam algumas "variáveis persistentes" que são privadas da função.
Portanto, a partir deste exemplo, podemos concluir que as condições para criar um fechamento são:
existem funções internas e externas. A função interna refere-se às variáveis locais da função externa. O objetivo principal do fechamento é definir. algumas funções. Variáveis persistentes limitadas por domínio, essas variáveis podem ser usadas para armazenamento em cache ou cálculos intermediários, etc.
Código Javascript
// Ferramenta de cache simples // A função anônima cria um fechamento const cache = (function() { loja const = {}; retornar { pegar(chave) { retornar loja[chave]; }, set(chave, val) { armazenar[chave] = val; } } }()); cache.set('a', 1); cache.get('a'); // 1O
exemplo acima é a implementação de uma ferramenta de cache simples. , não serão reciclados.
Desvantagens dos fechamentos: variáveis persistentes não serão liberadas normalmente e continuarão a ocupar espaço de memória, o que pode facilmente causar desperdício de memória, portanto, geralmente são necessários alguns mecanismos adicionais de limpeza manual.
função que aceita ou retorna uma função é chamada de função de ordem superior.
Parece uma palavra muito fria, mas na verdade a usamos com frequência, mas simplesmente não sabemos seus nomes. A linguagem JavaScript oferece suporte nativo a funções de ordem superior, porque as funções JavaScript são cidadãos de primeira classe e podem ser usadas como parâmetros e como valor de retorno de outra função.
Muitas vezes podemos ver muitas funções nativas de alta ordem em JavaScript, como Array.map, Array.reduce e Array.filter.
Vamos ver como ele usa
é uma coleção de
).Em outras palavras, cada item do conjunto é submetido à mesma transformação para gerar um novo
mapa de conjunto. Como função de ordem superior, ela aceita um parâmetro de função como o
código Javascript
lógico do mapeamento// Adicione um a cada item em. o array para formar Um novo array // Método geral de escrita const arr = [1,2,3]; rs.push(++n } console.log(rs) // mapa reescreve const arr = [1,2,3]; const rs = arr.map(n => ++n);
o método de escrita geral acima, usar o loop for...of para percorrer a matriz fará com que operações adicionais, e existe o risco de alterar o array original
, mas a função map encapsula as operações necessárias, de modo que só precisamos nos preocupar com a implementação da função da lógica de mapeamento, o que reduz a quantidade de código e o risco de lado efeitos.
fornece alguns parâmetros de uma função e gera uma nova função que aceita outros parâmetros.
Você pode não ouvir esse termo com frequência, mas qualquer pessoa que tenha usado undescore ou lodash já o viu.
Existe uma função mágica _.partial, que é
o código Javascript
curry// Obtém o caminho relativo do arquivo de destino para o caminho base // O método geral de escrita é const BASE = '/path/to/base'; caminho.relativo(BASE, '/algum/caminho'); // _.parical reescrita const BASE = '/caminho/para/base'; const relativoPath = relativoFromBase('/some/path');
Através de _.partial, obtemos a nova função relativoFromBase. Quando esta função é chamada, é equivalente a chamar path.relative, e o primeiro parâmetro é passado para BASE por padrão. Os parâmetros subsequentes passados são anexados em ordem.
Nesse caso, o que realmente queremos fazer é obter sempre o caminho relativo ao BASE, e não relativo a qualquer caminho. Currying nos permite nos preocupar apenas com alguns parâmetros de uma função, tornando o propósito da função mais claro e chamando-a mais simples.
combina os recursos de múltiplas funções para criar uma nova função
Você pode ter visto isso pela primeira vez no lodash, o método compose (agora chamado de fluxo)
Código Javascript
// Coloque cada palavra no array em maiúscula, faça Base64. //Método geral de escrita (um deles) const arr = ['pen', 'apple', 'applypen']; rs.push(btoa(w.toUpperCase())); console.log(rs); // _.flow rewrite const arr = ['pen', 'apple', 'applypen']; const upperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa)); console.log(upperAndBase64(arr));
_.flow mescla os recursos das funções de conversão de maiúsculas e de conversão Base64 para gerar uma nova função. Conveniente para usar como função de parâmetro ou reutilização posterior.
Do meu ponto de vista, meu entendimento da programação funcional JavaScript pode ser diferente de muitos conceitos tradicionais. Não creio apenas que funções de alta ordem sejam consideradas programação funcional. Outras, como chamadas combinadas de funções comuns, estruturas de cadeia, etc., pertencem à categoria de programação funcional, desde que utilizem funções como principais. operadora.
E acho que a programação funcional não é necessária, nem deveria ser uma regra ou requisito obrigatório. Assim como as ideias orientadas a objetos ou outras, também é uma das formas. Na maioria dos casos, deveríamos ser uma combinação de vários, em vez de limitados a conceitos.
Recomendações relacionadas: Tutorial de JavaScript
O texto acima é uma discussão detalhada sobre programação funcional de JavaScript. Para obter mais informações, preste atenção a outros artigos relacionados no site PHP chinês!