Cómo copiar objetos en JavaScript

C

Introducción

Una tarea muy común en la programación, independientemente del lenguaje, es copiar (o clonar) un objeto por valor, en lugar de copiar por referencia. La diferencia es que al copiar por valor, tiene dos objetos no relacionados con el mismo valor o datos. Copiar por referencia significa que tiene dos objetos que apuntan a los mismos datos en la memoria. Esto significa que si manipula el objeto A, por ejemplo, también manipulará el objeto B ya que ambos hacen referencia a los mismos datos subyacentes.

En este artículo, repasaré algunas de las formas en que puede copiar objetos por valor en JavaScript. Le mostraré cómo puede hacer esto usando bibliotecas de terceros y escribiendo su propia función de copia.

Nota: Dado que Node.js es solo un tiempo de ejecución creado en el motor JavaScript V8, todos los métodos de clonación que muestro en este artículo también funcionarán para Node.

Bibliotecas de terceros

Hay una serie de bibliotecas de terceros populares que tienen esta funcionalidad incorporada, que veremos en las siguientes secciones. En mi opinión, estas son la mejor solución para la mayoría de los casos de uso simples, ya que se han probado exhaustivamente y se actualizan continuamente. Escribir este tipo de código no es fácil, por lo que es muy útil poder usar un código que tenga muchos ojos en él.

Lodash

los Lodash Library proporciona algunos métodos diferentes para copiar o clonar objetos, según su caso de uso.

El método más genérico es el clone() método, que proporciona copias superficiales de objetos. Funciona simplemente pasando el objeto como primer argumento y se devolverá la copia:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.clone(arrays);
console.log(copy);
{ first: [ 1, 2, 3 ], second: [ 4, 5, 6 ] }

Esto significa que el objeto de “nivel superior” (o matriz, búfer, mapa, etc.) se clona, ​​pero los objetos más profundos se copiarán por referencia. El siguiente código demuestra que el first matriz en el original arrays objeto es el mismo objeto que el first matriz en el copy objeto:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.clone(arrays);
console.log(copy.first === arrays.first);
true

Si prefiere que todos los objetos, tanto superficiales como profundos, se copien, entonces querrá usar el cloneDeep() método en su lugar:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.cloneDeep(arrays);
console.log(copy);
{ first: [ 1, 2, 3 ], second: [ 4, 5, 6 ] }

Este método funciona mediante la clonación recursiva de todos los valores en cualquier nivel de profundidad.

Ejecutando la misma verificación de igualdad de arriba, podemos ver que las matrices original y copiada ya no son iguales ya que son copias únicas:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.cloneDeep(arrays);
console.log(copy.first === arrays.first);
false

Lodash ofrece algunos métodos de clonación más, que incluyen cloneWith() y cloneDeepWith(). Ambos métodos aceptan otro parámetro llamado customizer, que es una función que se utiliza para ayudar a producir el valor copiado.

Entonces, si desea usar alguna lógica de copia personalizada, puede pasar una función para manejarla dentro del método de Lodash. Por ejemplo, supongamos que tiene un objeto que contiene algunos Date objetos, pero desea que se conviertan en marcas de tiempo al ser copiados, puede hacerlo así:

const _ = require('lodash');

let tweet = {
    username: '@ScottWRobinson',
    text: 'I didn't actually tweet this',
    created_at: new Date('December 21, 2018'),
    updated_at: new Date('January 01, 2019'),
    deleted_at: new Date('February 28, 2019'),
};
let tweetCopy = l.cloneDeepWith(tweet, (val) => {
    if (l.isDate(val)) {
        return val.getTime();
    }
});
console.log(tweetCopy);
{ username: '@ScottWRobinson',
  text: 'I didn't actually tweet this',
  created_at: 1545372000000,
  updated_at: 1546322400000,
  deleted_at: 1551333600000 }

Como puede ver, los únicos datos que fueron alterados por nuestro método fueron los Date objetos, que ahora se han convertido en marcas de tiempo de Unix.

Guion bajo

los Guion bajo clone() El método funciona de la misma manera que el de Lodash. clone() método. Solo proporciona una copia superficial del objeto dado, y los objetos anidados se copian por referencia.

El mismo ejemplo que antes demuestra esto:

const _ = require('underscore');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.clone(arrays);
console.log(copy.first === arrays.first);
true

Desafortunadamente, la biblioteca de Underscore no parece tener ningún método para manejar la copia profunda. Puede implementar esta lógica por su cuenta (usando parte de la lógica que se muestra a continuación) y aún usar el de Underscore clone método para la copia superficial, o puede probar una de las otras soluciones de este artículo.

Soluciones personalizadas

Como mencioné anteriormente, asumir este desafío por sí mismo es difícil, ya que hay muchos casos (y casos extremos complicados) de manejar al clonar un objeto en JavaScript. Sin embargo, si se hace correctamente, podrá agregar una buena personalización dentro de su método que de otro modo no sería posible.

Usar métodos JSON

Una solución que se cita a menudo es simplemente utilizar el JSON.stringify y JSON.parse métodos a su favor, como este:

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = JSON.parse(JSON.stringify(arrays));
console.log(copy);
{ first: [ 1, 2, 3 ], second: [ 4, 5, 6 ] }

Esto te dejará con un objeto copiado profundamente y funciona muy bien para objetos simples que se convierten fácilmente a JSON.

Podemos verificar esto nuevamente usando la misma verificación que la anterior:

console.log(copy.first === arrays.first);
false

Si sabe que su objeto se puede serializar fácilmente, esta podría ser una buena solución para usted.

Escribiendo el tuyo desde cero

Si por alguna razón ninguna de las otras soluciones funciona para usted, tendrá que escribir su propio método de clonación.

Como no confío en mí mismo para implementar correctamente un método de clonación completa (y me arriesgo a que los lectores copien mis errores en su código de producción), he copiado la siguiente función de clonación de esta esencia, que copia objetos de forma recursiva y parece funcionar en muchos de los tipos de datos comunes con los que se ejecutará en JavaScript.

function clone(thing, opts) {
    var newObject = {};
    if (thing instanceof Array) {
        return thing.map(function (i) { return clone(i, opts); });
    } else if (thing instanceof Date) {
        return new Date(thing);
    } else if (thing instanceof RegExp) {
        return new RegExp(thing);
    } else if (thing instanceof Function) {
        return opts && opts.newFns ? new Function('return ' + thing.toString())() : thing;
    } else if (thing instanceof Object) {
        Object.keys(thing).forEach(function (key) { newObject[key] = clone(thing[key], opts); });
        return newObject;
    } else if ([ undefined, null ].indexOf(thing) > -1) {
        return thing;
    } else {
        if (thing.constructor.name === 'Symbol') {
            return Symbol(thing.toString().replace(/^Symbol(/, '').slice(0, -1));
        }
        return thing.__proto__.constructor(thing);
    }
}

Esta función funciona manejando casos específicos cuando es necesario (como matrices, expresiones regulares, funciones, etc.), y luego, para todos los demás tipos de datos (como números, cadenas, booleanos, etc.) por defecto es thingpropio constructor para copiar el valor. Si el thing es un objeto en sí mismo, entonces simplemente se llama recursivamente a sí mismo en los atributos secundarios de thing.

Consulte la esencia completa en el enlace anterior para ver todos los tipos de datos y casos extremos en los que se ha probado.

Conclusión

Si bien es simple en teoría, en la práctica copiar un objeto en JavaScript es todo menos simple. Afortunadamente, existen bastantes soluciones para que las use, como cloneDeep en Lodash, o incluso el incorporado JSON métodos. Y si por alguna razón ninguno de ellos es adecuado, entonces es posible escribir su propio método de clonación, siempre que lo pruebe a fondo.

Buena suerte y sabremos si tiene alguna idea, idea o sugerencia en los comentarios.

 

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con tus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad