JavaScript — Shallow copy and Deep copy

Mauricio Garcia
5 min readDec 1, 2019

--

Temario

  • Introducción
  • Shallow copy (Object.assign)
  • Deep copy (JSON (parse, stringify))

i. Introducción

He tenido la oportunidad de hacer bastantes entrevistas a desarrolladores de todos los niveles, y puedo decir que solo unos pocos saben y entienden bien la esencia de Shallow copy y Deep copy, lo menciono ya que es un tema bastante común que si no lo entiendes puede darte muchos dolores de cabeza e inclusive tener errores al manejar los datos.

Cuando vimos el tema de Tipo de valores[ref] hablamos de mutabilidad e inmutabilidad, mencionamos que hay dos formas de asignar una variable:

  1. Por valor (primitivos) — inmutabilidad
  2. Por referencia (objetos) — mutabilidad

ComprendimosCuando asignamos valores a los primitivos, el valor asignado realmente es una copia del valor asignado; y cuando asignamos valores a los complejos, realmente es una referencia al valor original.

Por ahora enfoquémonos en los valores por referencia:

En nuestro ejemplo, la variable obj y copy están haciendo referencia al mismo objeto, ya que si cambiamos obj.a o copy.a afecta a ambos, entonces, para romper la referencia de los objetos existen dos maneras de hacerlo: copia superficial o copia a profundidad; veamos qué significa cada uno de ellos…

iii. Shallow copy

Shallow copy (copiado superficial) es una copia de bits de un objeto; esto quiere decir que se crea un nuevo objeto que tiene una copia exacta de los valores en el objeto original; PERO si alguno de los campos del objeto son objetos, solo se van a copiar las referencias a las mismas.

a — Object.assign()

Para hacer una copia superficial del objeto, vamos a usar el método Object.assign() [ref], este se utiliza para copiar los valores de TODAS las propiedades propias ENUMERABLES (primer nivel) de un origen a un destino.

Ejemplo sencillo de como funciona:

En nuestro ejemplo vemos que efectivamente ya ambos objetos tienen asignado su propio lugar en memoria, y se comportan de manera independiente.

El método assign, nos permite hacer una copia de 2 o más objetos:

Ahora veamos un ejemplo, digamos, más real:

Imaginemos que tenemos una plantilla template, que la queremos usar para copiar valores de otro objeto. Entonces, tenemos un método llamado person, que va a recibir una variable data, y lo que se le mande va a fusionarlo con la variable template:

Bien hasta aquí creo que es bastante útil el método assign, pero… hay una parte de la definición dice lo siguiente:

…se utiliza para copiar los valores de TODAS las propiedades propias ENUMERABLES (primer nivel)

A que se refiere con propiedades propias ENUMERABLES (primer nivel), veamos el siguiente ejemplo:

Hagamos paso a paso el ejemplo y entendamos que esta pasando:

Tenemos el objeto de una persona_1, donde tiene name (string), age (number) y address (object):

Del lado izquierdo es nuestro objeto, y del lado derecho la simulación en memoria.

Ahora creamos la copia superficial (primer nivel) de persona_1 y la asignamos a persona_2:

Podemos observar en la imagen, que se ha copiado completamente el objeto de manera superficial (primer nivel: name, age), pero al tener otro objeto dentro (segundo nivel: address), este NO se va a copiar, si no que ambos harán referencia al mismo objeto; esto quiere decir que si cambiamos persona_1.address.city = ‘Morelos’, va a cambiar en ambos lados.

Entonces lo que debemos hacer es una copia exacta de TODO el objeto sin importar cuantos niveles tiene…

iv. Deep copy

Deep copy (copiado profundo) es una copia exacta del objeto entero (sin importar cuántos niveles de objetos tiene internamente), el original y la copia no compartirán nada.

Volvamos con el ejemplo anterior:

Nuestro objetivo es que cuando hagamos una copia, en memoria se vea algo así:

…y que si hacemos cambios en cualquier propiedad, SOLO le afecte al objeto indicado:

Para lograr esto, vamos a usar el objeto JSON con sus métodos parse y stringify.

a — JSON (parse, stringify)

El objeto JSON es un formato para almacenar y llevar datos, y que además tiene dos métodos bastante útiles para poder manejar su contenido:

  1. parse — Toma una cadena JSON y la transforma en objeto
  2. stringify — Toma un objeto y lo transforma en una cadena JSON

Veamos ejemplos con el método stringify:

Con el método stringify debemos de tener en cuenta las siguientes reglas:

  • Los objetos Boolean, Number, String se van a convertir en sus valores primitivos.
  • Si tiene undefined, Function, Symbol los va a omitir cuando estén dentro de un objeto y serán null cuando se encuentren dentro de un arreglo array.
  • Las propiedades que usen Symbol en los nombres se ignoran por completo.
  • La instancia de Date devuelve una cadena string.
  • Los números Infinity, NaN y null se consideran null.
  • El resto de las instancias de Object (Map, Set, etc), solo tendrán serializadas sus propiedades enumerables.

Para ver más ejemplos [ref].

Veamos ejemplos con el método parse:

Veamos un ejemplo más complejo, donde involucre más niveles:

Si queremos cambiar algún valor de myObjStr sin importar cual, se va a comportar de manera independiente a la propiedad myObj

En la siguiente entrega vamos a ver JavaScript — Array

La entrega pasada vimos POO — Closures & Currying

--

--