Introdução: por que esta pergunta reuniu milhares de visualizações no Stack Overflow
\n\nTodo mundo que já escreveu em JavaScript já lidou com arrays. Eles estão em toda parte: listas de produtos, filas de tarefas, histórico de ações do usuário. E mais cedo ou mais tarde surge a pergunta: “Como adicionar um elemento ao início de um array em JavaScript?” No Stack Overflow, esta pergunta acumulou centenas de milhares de visualizações e dezenas de respostas. Por quê? Porque por trás da aparente simplicidade, esconde-se uma camada inteira de conhecimento: desde desempenho até o funcionamento interno do motor.
\n\nNeste artigo, não vamos apenas mostrar unshift(). Vamos olhar “por baixo dos panos”: como o motor V8 lida com a inserção no início, por que unshift pode ser mais lento que push e quais alternativas existem para diferentes cenários. Vamos lá!
Método básico: o método unshift()
\n\nA maneira mais óbvia e comum é usar o método embutido unshift(). Ele adiciona um ou mais elementos ao início do array e retorna o novo comprimento do array.
let fruits = ['banana', 'orange'];\nlet newLength = fruits.unshift('apple');\nconsole.log(fruits); // ['apple', 'banana', 'orange']\nconsole.log(newLength); // 3\n\nVocê pode adicionar vários elementos de uma vez:
\n\nlet numbers = [3, 4, 5];\nnumbers.unshift(1, 2);\nconsole.log(numbers); // [1, 2, 3, 4, 5]\n\nPoderíamos parar por aqui. Mas vamos entender o que acontece internamente quando chamamos unshift().
Como o unshift() funciona por baixo dos panos: um olhar sobre o V8
\n\nUm array em JavaScript não é apenas um bloco contínuo de memória, como em C. Em motores modernos (V8 no Chrome/Node.js, SpiderMonkey no Firefox), os arrays podem ser implementados de duas maneiras:
\n\n- \n
- Fast elements (compactados) — um buffer contínuo, estilo C. Ideais para leitura e escrita por índice. \n
- Dictionary elements (esparsos) — uma tabela hash. Usados quando o array se torna “esburacado” (índices faltando) ou muito grande. \n
Quando você chama unshift() em um array fast, o motor faz o seguinte:
- \n
- Aloca nova memória (ou desloca a existente) pela quantidade de elementos adicionados. \n
- Desloca todos os elementos existentes para a direita em
nposições. \n - Escreve os novos elementos nas células liberadas no início. \n
Por que unshift é mais lento que push?
\n\nO método push() adiciona um elemento ao final — ele não precisa deslocar nada. Já o unshift() reescreve os índices de cada elemento existente. Se o array tiver 1000 elementos, o unshift é forçado a deslocar todos os 1000. O push simplesmente escreve no final.
No V8, há uma otimização: se o array estiver vazio ou quase vazio, o deslocamento é quase gratuito. Mas para arrays grandes, a diferença de desempenho pode ser de dezenas de vezes. Especialmente se você fizer unshift em um loop.
// Ruim: O(n²) devido ao deslocamento constante\nlet arr = [];\nfor (let i = 0; i < 10000; i++) {\n arr.unshift(i);\n}\n\n// Bom: O(n) — adiciona ao final, depois inverte\nlet arr2 = [];\nfor (let i = 0; i < 10000; i++) {\n arr2.push(i);\n}\narr2.reverse();\n\nAlternativa: operador spread (ES6+)
\n\nUma maneira moderna e elegante é usar o operador spread ... para criar um novo array:
let oldArray = [2, 3, 4];\nlet newArray = [1, ...oldArray];\nconsole.log(newArray); // [1, 2, 3, 4]\n\nEsta abordagem não muta o array original, o que é frequentemente útil em programação funcional (React, Redux). Mas lembre-se: um novo array é criado e os elementos antigos são copiados. Para arrays gigantescos, isso pode ser caro em termos de memória.
\n\nAdicionando vários elementos ao início
\n\nlet base =