JavaScript Avançado

Programação Funcional em JavaScript: Imutabilidade, Pureza e Composição: Do Básico ao Avançado

6 min de leitura

Programação Funcional em JavaScript: Imutabilidade, Pureza e Composição: Do Básico ao Avançado

Imutabilidade: O Fundamento da Programação Funcional A imutabilidade é o pilar central da programação funcional. Significa que, uma vez criado, um objeto ou dado nunca deve ser modificado. Em vez de alterar dados existentes, criamos novas versões deles. Isso elimina efeitos colaterais inesperados e torna o código mais previsível e testável. No JavaScript, a imutabilidade não é forçada nativamente, mas podemos implementá-la através de boas práticas. Observe: O spread operator ( ) e são aliados poderosos. Para arrays, usamos métodos que retornam novos arrays: Funções Puras: Previsibilidade e Testabilidade Uma função pura é aquela que, dado um mesmo input, sempre produz o mesmo output, sem depender de estado externo ou causar efeitos colaterais. Ela não modifica variáveis globais, não realiza I/O e não altera seus parâmetros. Funções puras são a essência do código funcional. Veja a diferença: Funções puras são imediatamente testáveis: Um exemplo mais prático com arrays: Composição: Construindo Poder com Simplicidade A composição funcional consiste em criar funções

<h2>Imutabilidade: O Fundamento da Programação Funcional</h2>

<p>A imutabilidade é o pilar central da programação funcional. Significa que, uma vez criado, um objeto ou dado nunca deve ser modificado. Em vez de alterar dados existentes, criamos novas versões deles. Isso elimina efeitos colaterais inesperados e torna o código mais previsível e testável.</p>

<p>No JavaScript, a imutabilidade não é forçada nativamente, mas podemos implementá-la através de boas práticas. Observe:</p>

<pre><code class="language-javascript"></code></pre>

<p>O spread operator (<code>...</code>) e <code>Object.assign()</code> são aliados poderosos. Para arrays, usamos métodos que retornam novos arrays:</p>

<pre><code class="language-javascript"></code></pre>

<h2>Funções Puras: Previsibilidade e Testabilidade</h2>

<p>Uma função pura é aquela que, dado um mesmo input, sempre produz o mesmo output, sem depender de estado externo ou causar efeitos colaterais. Ela não modifica variáveis globais, não realiza I/O e não altera seus parâmetros.</p>

<p>Funções puras são a essência do código funcional. Veja a diferença:</p>

<pre><code class="language-javascript"></code></pre>

<p>Funções puras são imediatamente testáveis:</p>

<pre><code class="language-javascript">function aplicarDesconto(preco, percentual) {

return preco * (1 - percentual / 100);

}

console.log(aplicarDesconto(100, 10)); // 90

console.log(aplicarDesconto(100, 10)); // 90 - determinístico!

// Fácil de testar

const resultado = aplicarDesconto(200, 20);

console.assert(resultado === 160, &#039;Desconto incorreto&#039;);</code></pre>

<p>Um exemplo mais prático com arrays:</p>

<pre><code class="language-javascript"></code></pre>

<h2>Composição: Construindo Poder com Simplicidade</h2>

<p>A composição funcional consiste em criar funções pequenas e focadas, depois combiná-las para resolver problemas complexos. Cada função faz uma coisa bem feita, e as conectamos como peças de um quebra-cabeça.</p>

<h3>Composição Básica</h3>

<pre><code class="language-javascript">// Funções simples e puras

const multiplicarPor2 = n =&gt; n * 2;

const somar5 = n =&gt; n + 5;

const converter = n =&gt; Resultado: ${n};

// Composição manual (de dentro para fora)

const valor = 10;

const resultado = converter(somar5(multiplicarPor2(valor)));

console.log(resultado); // &quot;Resultado: 25&quot;</code></pre>

<h3>Função de Composição (Composer)</h3>

<p>Para evitar o aninhamento excessivo, criamos uma função que compõe outras:</p>

<pre><code class="language-javascript">// Função auxiliar para compor

const compose = (...funcoes) =&gt; valor =&gt;

funcoes.reduceRight((acc, fn) =&gt; fn(acc), valor);

const multiplicarPor2 = n =&gt; n * 2;

const somar5 = n =&gt; n + 5;

const converter = n =&gt; Resultado: ${n};

const pipeline = compose(converter, somar5, multiplicarPor2);

console.log(pipeline(10)); // &quot;Resultado: 25&quot;</code></pre>

<h3>Pipe: Composição da Esquerda para Direita</h3>

<p>Às vezes é mais intuitivo ler de esquerda para direita:</p>

<pre><code class="language-javascript">const pipe = (...funcoes) =&gt; valor =&gt;

funcoes.reduce((acc, fn) =&gt; fn(acc), valor);

const multiplicarPor2 = n =&gt; n * 2;

const somar5 = n =&gt; n + 5;

const converter = n =&gt; Resultado: ${n};

const pipeline = pipe(multiplicarPor2, somar5, converter);

console.log(pipeline(10)); // &quot;Resultado: 25&quot;</code></pre>

<h3>Exemplo Prático: Processamento de Dados</h3>

<pre><code class="language-javascript">// Funções simples

const buscarUsuarios = () =&gt; [

{ id: 1, nome: &#039;Ana&#039;, idade: 25, ativo: true },

{ id: 2, nome: &#039;Bruno&#039;, idade: 30, ativo: false },

{ id: 3, nome: &#039;Carlos&#039;, idade: 28, ativo: true }

];

const apenasAtivos = usuarios =&gt; usuarios.filter(u =&gt; u.ativo);

const maioresQue26 = usuarios =&gt; usuarios.filter(u =&gt; u.idade &gt; 26);

const obterNomes = usuarios =&gt; usuarios.map(u =&gt; u.nome);

// Composição

const pipe = (...fns) =&gt; v =&gt; fns.reduce((acc, fn) =&gt; fn(acc), v);

const obterNomesValidos = pipe(

buscarUsuarios,

apenasAtivos,

maioresQue26,

obterNomes

);

console.log(obterNomesValidos()); // [&#039;Carlos&#039;]</code></pre>

<h2>Conclusão</h2>

<p>Três pilares transformam seu código em JavaScript funcional: <strong>Imutabilidade</strong> garante que dados não sejam alterados inesperadamente, reduzindo bugs. <strong>Funções puras</strong> tornam o código determinístico e testável, pois sempre produzem o mesmo resultado para o mesmo input. <strong>Composição</strong> permite construir soluções complexas juntando funções pequenas, focadas e reutilizáveis.</p>

<p>Esses conceitos não são apenas teóricos — melhoram a qualidade real do seu código, facilitam testes automatizados e tornam manutenção mais segura. Comece pequeno: escreva funções puras, evite mutações com spread operators e componha suas soluções. Com prática, a programação funcional se tornará sua segunda natureza.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://developer.mozilla.org/en-US/docs/Glossary/Functional_programming" target="_blank" rel="noopener noreferrer">MDN Web Docs - Functional Programming</a></li>

<li><a href="https://javascript.info/closure" target="_blank" rel="noopener noreferrer">JavaScript.info - Closures e Functional Programming</a></li>

<li><a href="https://medium.com/javascript-scene/composing-software-the-book-f31c77fc3ddc" target="_blank" rel="noopener noreferrer">Medium - Functional Programming in JavaScript</a></li>

<li><a href="https://frontendmasters.com/courses/functional-javascript/" target="_blank" rel="noopener noreferrer">Frontend Masters - Composing Software</a></li>

<li><a href="https://eloquentjavascript.net/05_higher_order.html" target="_blank" rel="noopener noreferrer">Eloquent JavaScript - Capítulo sobre Funções de Ordem Superior</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Prototype Chain Avançado: Object.create, getPrototypeOf e Herança Real: Do Básico ao Avançado
Prototype Chain Avançado: Object.create, getPrototypeOf e Herança Real: Do Básico ao Avançado

Object.create: A Base da Herança Real é o método fundamental para criar heran...

Guia Completo de Estratégias de Connection Pooling e Query Optimization em Node.js
Guia Completo de Estratégias de Connection Pooling e Query Optimization em Node.js

Connection Pooling em Node.js Connection pooling é uma técnica fundamental pa...

Guia Completo de Async Generators e Async Iterators em JavaScript na Prática
Guia Completo de Async Generators e Async Iterators em JavaScript na Prática

Entendendo Async Iterators Um async iterator é um objeto que implementa o pro...