Back

Arrays Não-Mutáveis: Escrevendo Código JavaScript Mais Seguro

Arrays Não-Mutáveis: Escrevendo Código JavaScript Mais Seguro

Quando você modifica um array em JavaScript, pode acidentalmente alterar dados dos quais outras partes do seu código dependem. Isso cria bugs difíceis de rastrear. A solução? Use métodos de array não-mutáveis que retornam novos arrays em vez de alterar o original.

Este artigo aborda os métodos essenciais de arrays não-mutáveis em JavaScript, por que eles são importantes para escrever código mais seguro e como usá-los efetivamente em seus projetos.

Pontos-Chave

  • Métodos de array não-mutáveis retornam novos arrays sem alterar os dados originais
  • Usar operações imutáveis previne efeitos colaterais inesperados e torna o código mais previsível
  • Métodos como map(), filter() e reduce() são alternativas mais seguras às operações mutáveis
  • O operador spread fornece uma sintaxe limpa para operações comuns de arrays

Por Que a Imutabilidade Importa em JavaScript

Mutar arrays pode causar comportamento inesperado em suas aplicações. Quando você passa um array para uma função ou o compartilha entre componentes, modificações em um local afetam todas as referências a esse array.

const originalTasks = ['Write code', 'Review PR', 'Deploy'];
const completedTasks = originalTasks;
completedTasks.push('Write tests');

console.log(originalTasks); // ['Write code', 'Review PR', 'Deploy', 'Write tests']
// Array original foi alterado!

Isso se torna especialmente problemático em aplicações React onde mutações de estado impedem que componentes sejam re-renderizados, ou no Redux onde o estado deve permanecer imutável.

Métodos Mutáveis vs Não-Mutáveis: Principais Diferenças

Métodos Mutáveis (Evite Estes)

  • push(), pop(), shift(), unshift() - Adiciona ou remove elementos
  • sort() - Ordena o array no local
  • reverse() - Inverte a ordem do array
  • splice() - Adiciona/remove elementos em qualquer posição
  • fill() - Preenche array com um valor

Métodos Não-Mutáveis (Use Estes)

  • map() - Transforma cada elemento
  • filter() - Mantém elementos que atendem uma condição
  • reduce() - Combina elementos em um único valor
  • slice() - Extrai uma porção do array
  • concat() - Combina arrays

Métodos Essenciais de Arrays Não-Mutáveis

map(): Transformar Sem Mutação

Em vez de usar um loop for que modifica um array, map() cria um novo array com valores transformados:

const prices = [10, 20, 30];
const discountedPrices = prices.map(price => price * 0.8);

console.log(prices);          // [10, 20, 30] - inalterado
console.log(discountedPrices); // [8, 16, 24]

filter(): Filtragem Segura de Arrays

Remove elementos sem tocar no array original:

const users = [
  { name: 'Alice', active: true },
  { name: 'Bob', active: false },
  { name: 'Charlie', active: true }
];

const activeUsers = users.filter(user => user.active);
console.log(users.length);       // 3 - original inalterado
console.log(activeUsers.length); // 2

reduce(): Combinar Sem Efeitos Colaterais

Calcula valores de arrays sem variáveis externas:

const orders = [
  { product: 'Laptop', price: 1200 },
  { product: 'Mouse', price: 25 }
];

const total = orders.reduce((sum, order) => sum + order.price, 0);
// Retorna 1225 sem modificar orders

slice(): Extrair Porções de Arrays

Obtenha um subconjunto de um array sem usar splice():

const tasks = ['Task 1', 'Task 2', 'Task 3', 'Task 4'];
const firstTwo = tasks.slice(0, 2);
const lastTwo = tasks.slice(-2);

console.log(firstTwo); // ['Task 1', 'Task 2']
console.log(lastTwo);  // ['Task 3', 'Task 4']
console.log(tasks);    // Original inalterado

concat(): Combinar Arrays com Segurança

Mescle arrays sem usar push():

const completed = ['Task 1', 'Task 2'];
const pending = ['Task 3', 'Task 4'];
const allTasks = completed.concat(pending);

// Ou use o operador spread
const allTasksSpread = [...completed, ...pending];

Melhores Práticas para Arrays Não-Mutáveis em JavaScript

1. Substitua Operações Mutáveis

// ❌ Evite: Mutação com push
const items = [1, 2, 3];
items.push(4);

// ✅ Melhor: Crie novo array
const newItems = [...items, 4];

2. Encadeie Métodos para Operações Complexas

const products = [
  { name: 'Laptop', price: 1200, inStock: true },
  { name: 'Phone', price: 800, inStock: false },
  { name: 'Tablet', price: 600, inStock: true }
];

const affordableInStock = products
  .filter(p => p.inStock)
  .filter(p => p.price < 1000)
  .map(p => p.name);
// Retorna ['Tablet'] sem modificar products

3. Use o Operador Spread para Operações Simples

// Remover item no índice
const removeAt = (arr, index) => [
  ...arr.slice(0, index),
  ...arr.slice(index + 1)
];

// Atualizar item no índice
const updateAt = (arr, index, value) => [
  ...arr.slice(0, index),
  value,
  ...arr.slice(index + 1)
];

Gerenciamento de Estado Mais Seguro no React

Métodos não-mutáveis são essenciais para atualizações de estado no React:

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React', done: false }
  ]);

  const toggleTodo = (id) => {
    // ✅ Cria novo array com objeto atualizado
    setTodos(todos.map(todo =>
      todo.id === id 
        ? { ...todo, done: !todo.done }
        : todo
    ));
  };

  const removeTodo = (id) => {
    // ✅ Filtra o todo sem mutação
    setTodos(todos.filter(todo => todo.id !== id));
  };
}

Considerações de Performance

Embora métodos não-mutáveis criem novos arrays, engines JavaScript modernas otimizam essas operações bem. Os benefícios de código previsível e livre de bugs geralmente superam pequenas diferenças de performance. Para código crítico em performance com grandes conjuntos de dados, considere usar bibliotecas especializadas como Immutable.js ou Immer.

Conclusão

Métodos de array não-mutáveis tornam seu código JavaScript mais previsível e fácil de debugar. Ao usar map(), filter(), reduce(), slice() e concat() em vez de suas contrapartes mutáveis, você evita efeitos colaterais que levam a bugs. Essa abordagem é especialmente valiosa em aplicações React e ao seguir princípios de programação funcional. Comece a substituir operações mutáveis em seu código hoje—seu eu futuro agradecerá.

Perguntas Frequentes

Sim, mas seja consistente dentro de cada contexto. Use métodos não-mutáveis para dados compartilhados, gerenciamento de estado e programação funcional. Métodos mutáveis podem ser aceitáveis para arrays temporários locais que não serão referenciados em outros lugares.

A diferença de performance é negligível para a maioria das aplicações. Engines JavaScript modernas otimizam essas operações eficientemente. Considere alternativas apenas para conjuntos de dados extremamente grandes ou loops críticos em performance após profiling confirmar um gargalo.

Use o operador spread ou slice para criar uma cópia primeiro, depois ordene a cópia. Por exemplo, const sorted = [...array].sort() ou const sorted = array.slice().sort(). Isso preserva a ordem do array original.

Slice é não-mutável e retorna um novo array contendo elementos extraídos sem alterar o original. Splice é mutável e modifica diretamente o array original removendo ou substituindo elementos e retorna os elementos removidos.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay