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()
ereduce()
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 elementossort()
- Ordena o array no localreverse()
- Inverte a ordem do arraysplice()
- Adiciona/remove elementos em qualquer posiçãofill()
- Preenche array com um valor
Métodos Não-Mutáveis (Use Estes)
map()
- Transforma cada elementofilter()
- Mantém elementos que atendem uma condiçãoreduce()
- Combina elementos em um único valorslice()
- Extrai uma porção do arrayconcat()
- 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];
Discover how at OpenReplay.com.
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.