Formas de Copiar Arreglos en JavaScript

javascript

🤔 ¿Por Qué Copiar un Arreglo?

A veces, queremos trabajar con los datos de un arreglo sin modificar el original. Si simplemente asignamos un arreglo a otra variable, no estamos creando una copia, sino que ambas variables apuntarán al mismo arreglo en la memoria.

let original = [1, 2, 3];
let referencia = original; // NO es una copia
referencia.push(4);
console.log(original); // Muestra [1, 2, 3, 4] ¡El original fue modificado!
console.log(referencia); // Muestra [1, 2, 3, 4]

Para evitar esto, necesitamos crear una copia independiente.

📋 Copias Superficiales (Shallow Copies)

La mayoría de los métodos integrados crean copias superficiales. Esto significa que copian los elementos del primer nivel del arreglo. Si esos elementos son valores primitivos (números, strings, booleanos), se copian por valor y son independientes. Pero si los elementos son objetos o arreglos, se copia la referencia a esos objetos/arreglos, no los objetos/arreglos en sí.

1. Usando slice()

Como vimos, llamar a slice() sin argumentos crea una copia superficial de todo el arreglo.

let numeros = [10, 20, 30];
let copiaNumeros = numeros.slice();
copiaNumeros.push(40);
console.log(numeros); // Muestra [10, 20, 30] (Original intacto)
console.log(copiaNumeros); // Muestra [10, 20, 30, 40]

2. Usando el Operador Spread (...)

El operador Spread es una forma moderna y muy legible de crear copias superficiales.

let colores = ["Rojo", "Verde", "Azul"];
let copiaColores = [...colores];
copiaColores[0] = "Morado";
console.log(colores); // Muestra ["Rojo", "Verde", "Azul"]
console.log(copiaColores); // Muestra ["Morado", "Verde", "Azul"]

3. Usando Array.from()

Array.from() crea una nueva instancia de Array a partir de un objeto iterable (como otro arreglo).

let herramientas = ["Martillo", "Sierra"];
let copiaHerramientas = Array.from(herramientas);
copiaHerramientas.pop(); // Quita "Sierra"
console.log(herramientas); // Muestra ["Martillo", "Sierra"]
console.log(copiaHerramientas); // Muestra ["Martillo"]

El Problema de las Copias Superficiales con Objetos/Arreglos Anidados

Si el arreglo contiene objetos o más arreglos, la copia superficial solo copia las referencias:

let originalAnidado = [{ id: 1 }, { id: 2 }];
let copiaSuperficial = [...originalAnidado];
// Modificamos un objeto DENTRO de la copia
copiaSuperficial[0].id = 99;
// ¡El cambio se refleja en el original también!
console.log(originalAnidado[0].id); // Muestra 99
console.log(copiaSuperficial[0].id); // Muestra 99
AVISO

⚠️ ¡Cuidado! Con arreglos anidados u objetos dentro de arreglos, slice(), ... y Array.from() no crean copias independientes de esos elementos internos.

🧱 Copias Profundas (Deep Copies)

Para crear una copia completamente independiente, incluyendo objetos y arreglos anidados, necesitamos una copia profunda. No hay un método integrado simple y universal para esto, pero hay técnicas comunes:

1. Usando JSON.stringify() y JSON.parse()

Una forma común (con limitaciones) es convertir el arreglo a un string JSON y luego de vuelta a un objeto/arreglo JavaScript.

let originalProfundo = [{ valor: 10 }, [{ sub: 20 }]];
let copiaProfunda = JSON.parse(JSON.stringify(originalProfundo));
// Modificamos un objeto anidado en la copia
copiaProfunda[1][0].sub = 500;
// El original NO se ve afectado
console.log(originalProfundo[1][0].sub); // Muestra 20
console.log(copiaProfunda[1][0].sub); // Muestra 500

Limitaciones: Este método no funciona bien con ciertos tipos de datos como Date, RegExp, Map, Set, funciones, o undefined, ya que se pierden o transforman durante la serialización JSON.

2. Usando structuredClone() (Moderno)

La forma más moderna y robusta es usar la función global structuredClone().

let datosOriginales = [{ fecha: new Date() }, { set: new Set([1, 2]) }];
let copiaEstructurada = structuredClone(datosOriginales);
// Modificamos el Set en la copia
copiaEstructurada[1].set.add(3);
// El original NO se ve afectado
console.log(datosOriginales[1].set); // Muestra Set(2) { 1, 2 }
console.log(copiaEstructurada[1].set); // Muestra Set(3) { 1, 2, 3 }
INFO

💡 structuredClone() es la mejor opción moderna para crear copias profundas, ya que maneja más tipos de datos que el truco de JSON.


☝️🤓
🏋️‍♂️ Ejercicio
  1. Crea un arreglo miArray = [5, 10, 15]. 2. Crea una referencia llamada refArray asignándole miArray. 3. Crea una copia superficial llamada copiaArray usando el operador Spread. 4. Modifica refArray agregando el número 20 al final (push(20)). 5. Modifica copiaArray cambiando el primer elemento a 99 (copiaArray[0] = 99). 6. Muestra por consola miArray, refArray y copiaArray. Explica por qué miArray cambió en un caso pero no en el otro.
🏋️‍♂️ Ejercicio

Dado el arreglo: let datos = [{ nombre: "Alice" }, { nombre: "Bob" }];

  1. Crea una copia superficial copia1 usando slice().
  2. Crea una copia profunda copia2 usando structuredClone().
  3. Cambia el nombre del primer objeto en copia1 a “Charlie” (copia1[0].nombre = "Charlie").
  4. Cambia el nombre del segundo objeto en copia2 a “David” (copia2[1].nombre = "David").
  5. Muestra por consola el arreglo datos original. ¿Cuál de las modificaciones (Charlie o David) afectó al original y por qué?
🚀 Extra

Investiga sobre: 1. Las limitaciones exactas del método JSON.parse(JSON.stringify()) para copias profundas (qué tipos de datos no maneja bien). 2. Bibliotecas de JavaScript populares (como Lodash) que ofrecen funciones para realizar copias profundas (ej. _.cloneDeep()). ¿Qué ventajas ofrecen sobre structuredClone() o el método JSON? 3. ¿Qué es la “copia por valor” vs “copia por referencia” en JavaScript?

Comentarios

Artículos relacionados

javascript

Extraer y Modificar Partes de Arreglos: slice() y splice()

4 mins
javascript

El Operador Spread (...) en Arreglos

4 mins