Introducción: por qué esta pregunta reunió miles de visitas en Stack Overflow
\n\nCualquiera que haya escrito en JavaScript al menos una vez se ha encontrado con arrays. Están en todas partes: listas de productos, colas de tareas, historial de acciones del usuario. Y tarde o temprano surge la pregunta: «¿Cómo agregar un elemento al inicio de un array en JavaScript?» En Stack Overflow, esta pregunta ha acumulado cientos de miles de visitas y decenas de respuestas. ¿Por qué? Porque detrás de la aparente simplicidad se esconde toda una capa de conocimiento: desde el rendimiento hasta el funcionamiento interno del motor.
\n\nEn este artículo no solo mostraremos unshift(). Nos adentraremos «bajo el capó»: cómo el motor V8 maneja la inserción al inicio, por qué unshift puede ser más lento que push y qué alternativas existen para diferentes escenarios. ¡Allá vamos!
Método básico: el método unshift()
\n\nLa forma más obvia y común es usar el método incorporado unshift(). Agrega uno o más elementos al inicio del array y devuelve la nueva longitud del array.
let fruits = ['banana', 'orange'];\nlet newLength = fruits.unshift('apple');\nconsole.log(fruits); // ['apple', 'banana', 'orange']\nconsole.log(newLength); // 3\n\nSe pueden agregar varios elementos a la vez:
\n\nlet numbers = [3, 4, 5];\nnumbers.unshift(1, 2);\nconsole.log(numbers); // [1, 2, 3, 4, 5]\n\nPodríamos terminar aquí. Pero analicemos qué sucede internamente cuando llamamos a unshift().
Cómo funciona unshift() bajo el capó: una mirada a V8
\n\nUn array en JavaScript no es simplemente un bloque continuo de memoria, como en C. En los motores modernos (V8 en Chrome/Node.js, SpiderMonkey en Firefox), los arrays pueden implementarse de dos maneras:
\n\n- \n
- Fast elements (empaquetados) — un búfer continuo, al estilo C. Óptimos para lectura y escritura por índice. \n
- Dictionary elements (dispersos) — una tabla hash. Se usan cuando el array se vuelve «con agujeros» (índices faltantes) o demasiado grande. \n
Cuando llamas a unshift() en un array fast, el motor hace lo siguiente:
- \n
- Asigna nueva memoria (o desplaza la existente) por la cantidad de elementos a agregar. \n
- Desplaza todos los elementos existentes hacia la derecha
nposiciones. \n - Escribe los nuevos elementos en las celdas liberadas al inicio. \n
¿Por qué unshift es más lento que push?
\n\nEl método push() agrega un elemento al final; no necesita desplazar nada. unshift(), en cambio, reescribe los índices de cada elemento existente. Si el array tiene 1000 elementos, unshift se ve obligado a desplazar los 1000. push simplemente escribe al final.
En V8 hay una optimización: si el array está vacío o casi vacío, el desplazamiento es casi gratuito. Pero para arrays grandes, la diferencia de rendimiento puede ser de decenas de veces. Especialmente si se hace unshift en un bucle.
// Malo: O(n²) debido al desplazamiento constante\nlet arr = [];\nfor (let i = 0; i < 10000; i++) {\n arr.unshift(i);\n}\n\n// Bueno: O(n) — agregamos al final, luego invertimos\nlet arr2 = [];\nfor (let i = 0; i < 10000; i++) {\n arr2.push(i);\n}\narr2.reverse();\n\nAlternativa: operador spread (ES6+)
\n\nUna forma moderna y elegante es usar el operador spread ... para crear un nuevo array:
let oldArray = [2, 3, 4];\nlet newArray = [1, ...oldArray];\nconsole.log(newArray); // [1, 2, 3, 4]\n\nEste enfoque no muta el array original, lo que suele ser útil en programación funcional (React, Redux). Pero recuerde: se crea un nuevo array y los elementos antiguos se copian. Para arrays gigantescos, esto puede ser costoso en memoria.
\n\nAgregar varios elementos al inicio
\n\nlet base =