Artículo original escrito por Yazeed Bzadough
Artículo original How to clone an array in JavaScript
Traducido y adaptado por Rafael D. Hernandez

1. Operador de propagación (Copia superficial)

Desde que se eliminó ES6, este ha sido el método más popular. Es una sintaxis breve y la encontrarás increíblemente útil cuando uses librerías como React y Redux.

numeros = [1, 2, 3];
copiaNumeros = [...numeros];

Nota: Esto no copia de forma segura arreglos multidimensionales. Los valores de arreglo/objeto se copian por referencia en lugar de por valor.

Esto está bien

copiaNueros.push(4);
console.log(numeros, copiaNumeros);
// [1, 2, 3] and [1, 2, 3, 4]
// numeros deja intacto

Esto no está bien

numerosAnidados = [[1], [2]];
copiaNumeros = [...numerosAnidados];

copiaNumeros[0].push(300);
console.log(numerosAnidados, copiaNumeros);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Ambos han sido cambiados porque comparten referencias

2. El bucle for() (Copia superficial)

Imagino que este enfoque es el menos popular, dado lo moderna que se ha vuelto la programación funcional en nuestros círculos.

Puro o impuro, declarativo o imperativo, ¡hace el trabajo!

numeros = [1, 2, 3];
copiaNumeros = [];

for (i = 0; i < numeros.length; i++) {
  copiaNumeros[i] = numeros[i];
}

Nota: Esto no copia de forma segura arreglos multidimensionales. Dado que está utilizando el operador =, asignará objetos/arreglos por referencia en lugar de por valor.

Esto está bien

copiaNumeros.push(4);
console.log(numeros, copiaNumeros);
// [1, 2, 3] and [1, 2, 3, 4]
// los números se quedan solos

Esto no está bien

numerosAnidados = [[1], [2]];
copiaNumeros = [];

for (i = 0; i < numerosAnidados.length; i++) {
  copiaNumeros[i] = numerosAnidados[i];
}

copiaNumeros[0].push(300);
console.log(numerosAnidados, copiaNumeros);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Ambos han sido cambiados porque comparten referencias

3. Bucle while() (Copia superficial)

Lo mismo que el bucle for — impuro, imperativo, bla, bla, bla... ¡funciona! ?

numeros = [1, 2, 3];
copiaNumeros = [];
i = -1;

while (++i < numeros.length) {
  copiaNumeros[i] = num[i];
}

Nota: Esto también asigna objetos/arreglos por referencia en lugar de por valor.

Esto está bien

copiaNumeros.push(4);
console.log(numeros, copiaNumeros);
// [1, 2, 3] y [1, 2, 3, 4]
// los números se quedan solos

Esto no está bien

numerosAnidados = [[1], [2]];
copiaNumeros = [];

i = -1;

while (++i < numerosAnidados.length) {
  copiaNumeros[i] = numerosAnidados[i];
}

copiaNumeros[0].push(300);
console.log(numerosAnidados, copiaNumeros);
// [[1, 300], [2]]
// [[1, 300], [2]]
// Ambos han sido cambiados porque comparten referencias

4. Array.map (Copia superficial)

De vuelta en territorio moderno, encontraremos la función de iteración map. Arraigado en las matemáticas, map es el concepto de transformar un conjunto en otro tipo de conjunto, preservando al mismo tiempo la estructura.

En español, eso significa Array.map devuelve un arreglo de la misma longitud cada vez.

Para duplicar una lista de números, utiliza map con una función doble.

numeros = [1, 2, 3];
doble = (x) => x * 2;

numeros.map(doble);

¿Y la clonación??

Es cierto que este artículo trata sobre la clonación de arreglos. Para duplicar un arreglo, simplemente devuelve el elemento de la llamada de map.

numeros = [1, 2, 3];
copiaNumeros = numeros.map((x) => x);

Si quieres ser un poco más matemático, (x) => x se llama identidad. Devuelve cualquier parámetro que se le haya dado.

map(identidad) clona una lista.

identidad = (x) => x;
numeros.map(identidad);
// [1, 2, 3]

Nota: Esto también asigna objetos/arreglos por referencia en lugar de por valor.

5. Array.filter (Copia superficial)

Esta función devuelve un arreglo, igual que map, pero no se garantiza que tenga la misma longitud.

¿Qué pasa si filtras números pares?

[1, 2, 3].filter((x) => x % 2 === 0);
// [2]

La longitud del arreglo de entrada era 3, pero la longitud resultante es 1.

Sin embargo, si el predicado de tu filter siempre devuelve true, sin embargo, ¡obtendrás un duplicado!

numeros = [1, 2, 3];
copiaNumeros = numeros.filter(() => true);

Cada elemento pasa la prueba, por lo que se devuelve.

Nota: Esto también asigna objetos/arreglos por referencia en lugar de por valor.

6. Array.reduce (Copia superficial)

Casi me siento mal usando reduce para clonar un arreglo, porque es mucho más potente que eso. Pero aquí vamos…

numeros = [1, 2, 3];

copiaNumeros = numeros.reduce((nuevoArreglo, elemento) => {
  nuevoArreglo.push(elemento);

  return nuevoArreglo;
}, []);

reduce transforma un valor inicial a medida que recorre una lista.

Aquí el valor inicial es un arreglo vacío, y lo estamos llenando con cada elemento a medida que avanzamos. Este arreglo debe devolverse desde la función que se utilizará en la siguiente iteración.

Nota: Esto también asigna objetos/arreglo por referencia en lugar de por valor.

7. Array.slice (Copia superficial)

slice devuelve una copia superficial de un arreglo basado en el índice de inicio/fin proporcionado por ti.

Si queremos los primeros 3 elementos:

[1, 2, 3, 4, 5].slice(0, 3);
// [1, 2, 3]
// Comienza en el índice 0, se detiene en el índice 3

Si queremos todos los elementos, no des ningún parámetro

numeros = [1, 2, 3, 4, 5];
copiaNumeros = numeros.slice();
// [1, 2, 3, 4, 5]

Nota: Se trata de una copia superficial, por lo que también asigna objetos/arreglos por referencia en lugar de por valor.

8. JSON.parse y JSON.stringify (Copia profunda)

JSON.stringify convierte un objeto en una cadena.

JSON.parse convierte una cadena en un objeto.

Combinándolos pueden convertir un objeto en una cadena y, después, invertir el proceso para crear una nueva estructura de datos.

Nota: ¡Este copia de forma segura objetos/arreglos profundamente anidados!

numerosAnidados= [[1], [2]];
copiaNumeros = JSON.parse(JSON.stringify(numerosAnidados));

copiaNumeros[0].push(300);
console.log(numerosAnidados, copiaNumeros);

// [[1], [2]]
// [[1, 300], [2]]
// ¡Estas dos arreglos están completamente separadas!

9. Array.concat (Copia superficial)

concat combina arreglos con valores u otros arreglos.

[1, 2, 3].concat(4); // [1, 2, 3, 4]
[1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5]

Si no das nada o un arreglo vacío, se devuelve una copia superficial.

[1, 2, 3].concat(); // [1, 2, 3]
[1, 2, 3].concat([]); // [1, 2, 3]

Nota: Esto también asigna objetos/arreglos por referencia en lugar de por valor.

10. Array.from (Copia superficial)

Esto puede convertir cualquier objeto iterable en un arreglo. Dando un arreglo devuelve una copia superficial.

numeros = [1, 2, 3];
copiaNumeros = Array.from(numeros);
// [1, 2, 3]

Nota: Esto también asigna objetos/arreglos por referencia en lugar de por valor.

Conclusión

Bueno, ¿fue divertido?

Traté de clonar usando solo 1 paso. Encontrarás muchas más maneras si empleas múltiples métodos y técnicas.