Seleccionar p谩gina

En Javascript los objetos son siempre pasados por referencia entre una variable y otra, por lo que no es evidente la forma en la que se pueden copiar objetos y obtener una nueva entidad independiente. Dependiendo de que otros lenguajes de programaci贸n se han utilizado antes, esta caracter铆stica puede parecer m谩s o menos extra帽a, pero en todos los casos, este comportamiento suele provocar problemas y efectos colaterales no deseados, especialmente cuando, por ejemplo, una funci贸n modifica sin avisar un objeto que ha sido pasado c贸mo par谩metro.

Por ejemplo, en este sencillo caso parece que estamos copiando el valor de a en b, pero lo que estamos haciendo es obtener otra nueva referencia al mismo objeto. De esta forma, cuando cambiamos a[0] tambi茅n estamos cambiando b[0]:

const a = [1,2,3,4,5];
const b = a;

a[0] = 'uno';
console.assert(b[0] === 'uno');

En Javascript se pueden 芦copiar禄 objetos, aunque no dispongamos de un operador para ello. Vamos a ver paso a paso c贸mo se hace.

Operador de propagaci贸n (spread operator)

En ES2015 apareci贸 el operador de propagaci贸n o spread operator (en ingl茅s). Este operador facilita mucho la copia del contenido de un Array en otro:

const a = [1,2,3,4,5];
const b = [...a];
console.assert(a !== b);

En ES2018 se incluy贸 este mismo operador de propagaci贸n para los objetos literales y, por lo tanto, podemos con mucha facilidad copiar los valores de los objetos, es decir, las propiedades de uno en otro:

const x = {a: 1, b: 2, c: 3};
const y = {...x};
console.assert(x !== y);

En ambos casos, conseguimos lo que est谩bamos buscando: copiar los valores del objeto. Esta operaci贸n se puede realizar por otros medios, como el m茅todo .slice() de los Array o el m茅todo Object.assign(), pero creo que todos coincidiremos que el operador de propagaci贸n es muy sencillo de utilizar y bastante elegante.

Copia superficial vs. copia en profundidad

Cuando copiamos un objeto o una matriz, estamos haciendo una copia superficial de los valores del objeto, es decir, s贸lo estamos duplicando los valores del primer nivel de las propiedades del propio objeto y mantenemos intactos los objetos que pudieran existir a otros niveles de profundidad como objetos dentro de las propiedades.

const x = {a : {a : 1}, b : {b : 2}, c : {c : 3}};
const y = {...x};
console.assert (x.a !== y.a);	// Assertion failed

La soluci贸n es sencilla, para poder hacer es copiar objetos en profundidad debemos crear una peque帽a funci贸n que de forma recursiva recorra los elementos del objeto y aplique el m茅todo de copia que estemos utilizando:

function copy (obj) {
  let result;
  if (obj instanceof Array) {
    result = [ ...obj ];
  } else if (typeof obj === 'object') {
    result = {...obj}
  } else {
    return obj;
  }
  for (let prop of Reflect.ownKeys (result)) {
    result[ prop ] = copy (result[ prop ]);
  }
  return result;
}

const a = [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ];
const b = copy (a);
console.assert (a[ 0 ] !== b[ 0 ]);

const x = {a : {a : 1}, b : {b : 2}, c : {c : 3}};
const y = copy (x);
console.assert (x.a !== y.a);

Subclases que heredan de las clases predefinidas

Al hacer este tipo de funciones se nos suele olvidar que en ES2015 en adelante es posible crear clases derivadas de objetos predefinidos y que, por ejemplo, podemos crear una clase MyArray que herede de Array:

class MyArray extends Array {
  intersection(arr) {
    return new MyArray(...this.filter(value => arr.includes(value)));
  }
}

Si aplicamos la funci贸n copy() que hicimos en la secci贸n anterior a este tipo de objeto, obtendremos un objeto Array y no un objeto MyArray:

const x = copy(a);
console.assert(x instanceof MyArray);    // Assertion failed

La soluci贸n es muy sencilla, s贸lo tenemos que cambiar de estrategia para hacer la creaci贸n del nuevo objeto utilizando el mismo constructor y realizar copia de los valores de sus propiedades. La funci贸n queda de esta forma:

function copy (obj) {
  let result;
  if (typeof obj === 'object') {
    result = new obj.constructor();
  } else {
    return obj;
  }
  for (let prop of Reflect.ownKeys (obj)) {
    result[ prop ] = copy (obj[ prop ]);
  }
  return result;
}

Con esta nueva funci贸n se permite copiar objetos Array, Object y las clases que hereden de ellos:

class MyArray extends Array {
  intersection(arr) {
    return new MyArray(...this.filter(value => arr.includes(value)));
  }
}

class MyObject extends Object {
  constructor() {
    super();
    this.class = 'MyModel';
  }
}

const a = new MyArray(1,2,3,4,5);
const b = copy(a);
console.assert(b instanceof MyArray);
console.assert(a != b);

const x = new MyObject();
const y = copy(x);
console.assert(y instanceof MyObject)
console.assert(a != b)

El caso especial de null

Aunque el c贸digo anterior tiene muy buen aspecto, lo cierto es que escode un problema importante: no funciona si el objeto es null. Este valor es un objeto, pero carece de constructor, por lo que la llamada a obj.constructor() lanzar谩 una excepci贸n.

const x = {n : null};
const y = copy (x);	// TypeError: Cannot read property 'constructor' of null

Bueno, s贸lo tenemos que incluir unas pocas l铆neas para controlar este caso:

function copy (obj) {
  let result;
  if (obj === null) {
    return obj;
  }
  if (typeof obj === 'object') {
    result = new obj.constructor ();
  } else {
    return obj;
  }
  for (let prop of Reflect.ownKeys (obj)) {
    result[ prop ] = copy (obj[ prop ]);
  }
  return result;
}

El objeto Date

Seguramente alguno de vosotros se habr铆a dado cuenta, nos hemos olvidado del objeto Date. Las fechas en Javascript son un tipo de objeto y, por lo tanto, debemos encontrar una forma correcta para copiar objetos de tipo fecha y no pasarlos por valor al nuevo objeto. En nuestro c贸digo, aunque se llama a su constructor, no estamos estableciendo la fecha adecuada, ya que el valor de la fecha se debe incluir en la llamada al constructor y no basta con copiar el valor de las propiedades para igualar el valor interno de la fecha.

Nuevamente son unas pocas l铆neas m谩s de c贸digo las que tenemos que incluir para controlar este nuevo caso, comprobando que estamos ante un objeto que hereda de Date:

function copy (obj) {
  let result;
  if (obj === null) {
    return obj;
  }
  if (obj instanceof Date) {
    result = new obj.constructor (obj.valueOf());
  } else if (typeof obj === 'object') {
    result = new obj.constructor ();
  } else {
    return obj;
  }
  for (let prop of Reflect.ownKeys (obj)) {
    result[ prop ] = copy (obj[ prop ]);
  }
  return result;
}

隆Alto! 驴pero cuantos tipos de objetos hay en Javascript?

Ahora que nos hemos percatado de que nos hab铆amos olvidado del objeto Date, podemos preguntarnos qu茅 otros tipos de objetos nos hemos olvidado y que pueden ser susceptibles de ser copiados en Javascript, pueden estar contenidos en una matriz, en un objeto o simplemente necesitamos clonarlos. Para hacer una funci贸n consistente para copiar objetos deber铆amos tener una respuesta razonable para todos los casos y no s贸lo para unos pocos.

Lo cierto es que la lista de tipos de objetos que podemos instanciar es bastante grande, mucho m谩s de lo que solemos pensar. De forma resumida incluimos aqu铆 esta referencia:

  • Objectos b谩sicos:
    • Object, tambi茅n se puede utilizar la expresi贸n literal { }.
    • Array, tambi茅n se puede utilizar la expresi贸n literal [ ].
    • Date, objeto fecha.
  • Recubrimiento de los tipos primitivos, normalmente no se utilizan con new,
    pero si se hace, se crea un objeto que recubre el valor primitivo:
    • Boolean, recubre un tipo l贸gico.
    • Number, recubre un tipo num茅rico.
    • String, recubre un tipo cadena de texto.
  • Estructuras de datos:
    • Map, es una colecci贸n de pares clave/valor donde cualquier tipo puede usarse como clave o como valor..
    • Set, permite almacenar valores 煤nicos de cualquier tipo, ya sean valores primitivos u objetos.
    • WeakMap, es una colecci贸n de pares clave/valor en el que las claves son siempre objetos y est谩n d茅bilmente referenciados.
    • WeakSet, permite almacenar objetos levemente referenciados.
  • Errores, son objetos de diferente tipo, aunque todos heredan de Error:
    • Error, error gen茅rico.
    • EvalError, existe por compatibilidad hacia atr谩s
    • RangeError, error que indica que un valor no est谩 dentro de rango aceptado.
    • ReferenceError, error que se produce cuando se usa una variable no creada.
    • SyntaxError, se produce cuando hay un error de sintaxis.
    • TypeError, se lanza cuando el tipo no es el esperado.
    • URIError, se lanza cuando se intenta utilizar una URI mal formada.
  • Funciones:
    • Function, rara vez se utiliza con new y casi siempre se utiliza una sintaxis como function () {} o () => {}.
    • GeneratorFunction, no se puede utilizar con new, siempre se utiliza una sintaxis function * () {}.
    • AsyncFunction, no se puede utilizar con new, siempre se utiliza una sintaxis async function () {}.
  • Expresiones regulares, para gestionar patrones en cadenas de texto:
    • RegExp, se puede crear tambi茅n con la expresi贸n literal / /.
  • Captura de operaciones con los objetos:
  • Control de ejecuci贸n:
    • Promise, devuelve un objeto para manejar la ejecuci贸n as铆ncrona de una funci贸n.
    • Array Iterator, no se puede crear directamente, se utiliza para iterar un Array.
    • AsyncGenerator, no se puede crear directamente, se devuelve por una funci贸n as铆ncrona y generadora.
    • Generator, no se puede crear directamente, se devuelve por las funciones generadoras.
    • String Iterator, no se puede crear directamente, se devuelve al iterar una cadena de texto.
    • Async Iterator, no se puede crear directamente, es utilizado en los bucles for wait of.
    • Map Iterator, no se puede crear directamente, se devuelve al iterar un Map.
    • Set Iterator, no se puede crear directamente, se devuelve al iterar un Set.
    • RegExp String Iterator, no se puede crear directamente, se devuelve al ejecutar el m茅todo .matchAll().
  • Manejo de datos binarios:
    • ArrayBuffer, crea un buffer de tama帽o fijo para el manejo de datos binarios.
    • SharedArrayBuffer, crea un buffer de tama帽o fijo compartido y no eliminable para el manejo de datos binarios.
    • DataView, permite manejar un ArrayBuffer o SharedArrayBuffer a bajo nivel.
    • TypedArray, no se puede crear directamente, de 茅l heredan los siguientes objetos:
      • BigInt64Array, manejo del buffer como enteros de 64 bits.
      • BigUint64Array, manejo del buffer como enteros de 64 bits sin signo.
      • Float32Array, manejo del buffer como n煤mero de coma flotante de 32 bits.
      • Float64Array, manejo del buffer como n煤mero de coma flotante de 64 bits.
      • Int8Array, manejo del buffer como enteros de 8 bits.
      • Int16Array, manejo del buffer como enteros de 16 bits.
      • Int32Array, manejo del buffer como enteros de 32 bits.
      • Uint8Array, manejo del buffer como enteros de 8 bits sin signo.
      • Uint8ClampedArray, manejo del buffer como enteros de 8 bits sin signo con valores entre 0 y 255.
      • Uint16Array, manejo del buffer como enteros de 16 bits sin signo.
      • Uint32Array, manejo del buffer como enteros de 32 bits sin signo.
  • Objectos globales, de los que no se puede crear una nueva instancia:
    • Atomics, contiene m茅todos para el manejo de un ShareArrayBuffer.
    • JSON, contiene m茅todos para la interpreaci贸n y creaci贸n de cadenas de texto con formato JSON.
    • Math, contiene m茅todos y constantes matem谩ticas.
    • Reflect, contiene m茅todos para reproducir operaciones interceptadas, habitualmente en un Proxy.
    • globalThis, corresponde con window en los navegaores y con root en Node y es donde residen las variables globales.
    • null, objeto con valor null
    • Intl, contiene los objetos del ECMAScript Internationalization API.

A estas clases de objetos se deber铆an a帽adir los tipos de objetos que creemos nosotros con class, ya sea heredando de Object o de cualquiera de los constructores predefinidos. La naturaleza y dise帽o de estas clases puede ser muy variado y hay que tener en cuenta las caracter铆sticas concretas de cada uno.

Tambi茅n es importante tener en cuenta dos situaciones peculiares que se pueden producir al utilizar Object.create()

  • Object.create({ }), devuelve un objeto cuyo constructor es Object pero cuyo prototipo es el objeto pasado como par谩metro.
  • Object.create(null), devuelve un objeto que no hereda de Object y que tiene como prototipo null, pero carece de constructor.

Estos objetos tienen un comportamiento especial y debemos conocerlo.

Tambi茅n tenemos que recordar que los objetos pueden hacer referencia a si mismos, es decir, tener referencias circulares. Si no recordamos est谩 caracter铆stica seguramente tengamos algunos problemas.

Por ultimo, tenemos que recordar que las propiedades de los objetos se pueden definir y configurar con Object.defineProperty(), indicando si son enumerables o no, si son de s贸lo lectura o est谩n gestionadas con un m茅todo get y un m茅todo set(), etc. Cuando trabajemos con las propiedades es conveniente que utilicemos sus descriptores y no s贸lo los creemos directamente.

Ahora tenemos que volver a preguntarnos, 驴c贸mo es posible copiar objetos de todos estos tipos teniendo en cuenta sus diferentes peculiaridades y caracter铆sticas?

Una funci贸n para copiarlos a todos

Aunque parezca dif铆cil, lo cierto es que podemos crear con bastante facilidad una funci贸n para copiar objetos correctamente independientemente del tipo que sean y de las caracter铆sticas de que dispongan. Vamos a ver c贸mo.

En primer lugar, tenemos que aclarar que algunos objetos no tiene mucho sentido que sean copiados. Por ejemplo, copiar funciones (que son objetos) no parece tener mucha utilidad. Podr铆amos obtener su c贸digo y volverlo a evaluar, pero no es algo que tenga mucho sentido en la pr谩ctica. Tampoco tiene sentido que copiemos los objetos que se utilizan para gestionar el control de ejecuci贸n como Promise, los objetos que manejan los iteradores o los devueltos por las funciones generadoras. El estado de estos objetos debe mantenerse de forma 煤nica y obtener un duplicado s贸lo puede producir problemas. Por 煤ltimo, tenemos que comprender que no se pueden copiar objetos de tipo WeakMap y WeakSet , ya que carecemos de mecanismos para recorrer su contenido y s贸lo podemos acceder a sus elementos si conocemos previamente las claves que contienen.

Tambi茅n tenemos que tener en cuenta que copiar objetos de clases personalizadas puede llegar a ser bastante complejo, sobre todo si establecen constructores que reciben los datos por par谩metro y no reflejan los mismos en propiedades p煤blicas que podamos copiar. Esto ocurre tanto en clases que heredan de Object como las que heredan de otros constructores. Para estos casos vamos a crear un s铆mbolo bien conocido para implementar en nuestras clases un m茅todo que permita la copia de los objetos. Si la clase implementa un m茅todo con el s铆mbolo, se utilizar谩 para hacer la copia del objeto. No es una soluci贸n universal, pero al menos podemos aplicarla en nuestras clases.

Ahora que tenemos claro que cosas no vamos a copiar y que limitaciones tenemos, podemos mostrar el c贸digo de esta funci贸n que puede copiar objetos de cualquier tipo:

const copy = (function () {
  const TypedArray           = Object.getPrototypeOf (Int32Array);
  const hasNode              = typeof Node !== 'undefined';
  const hasSharedArrayBuffer = typeof SharedArrayBuffer !== 'undefined';
  const ignoreTypes          = [ 'Array Iterator', 'AsyncGenerator', 'Generator',
    'String Iterator', 'Async Iterator', 'Map Iterator', 'Set Iterator',
    'RegExp String Iterator', 'Promise', 'WeakMap', 'WeakSet' ];

  const stack = new Map();                                 // Stack for circular objects
  function copy (obj) {
    if (typeof obj !== 'object') {
      return obj;
    }
    if (obj === null ||                                    // null or ignore object type
        ignoreTypes.includes (obj[ Symbol.toStringTag ]))
    {
      return obj;
    }
    if (stack.has(obj)) {                                  // Check if it's a circular object
      return stack.get(obj);
    }
    let result;
    let proto = Object.getPrototypeOf (obj);
    if (obj[ copy.symbol ]) {                              // Well kwon symbol for copy
      result = obj[ copy.symbol ] ();
    } else if (proto === Object.prototype) {               // Simple object
      result = {};
    } else if (proto === null) {                           // Create.object(null)
      result = Object.create (null);
    } else if (obj instanceof Date ||                      // Date, Boolean, String, Number
               obj instanceof Boolean ||
               obj instanceof Number ||
               obj instanceof String)
    {
      result = new (obj.constructor) (obj.valueOf ());
    } else if (obj instanceof Map) {                       // Map
      result = new (obj.constructor) ();
      for (let key of obj.keys ()) {
        result.set (key, copy (obj.get (key)));
      }
    } else if (obj instanceof Set) {                       // Set
      result = new (obj.constructor) ();
      for (let value of obj.values ()) {
        result.add (copy (value));
      }
    } else if (obj instanceof ArrayBuffer ||               // ArrayBuffer, SharedArrayBuffer
               (hasSharedArrayBuffer && obj instanceof SharedArrayBuffer))
    {
      result = obj.slice (0);
    } else if (obj instanceof DataView) {                  // DataView
      result = new (obj.constructor) (obj.buffer.slice (0));
    } else if (obj instanceof TypedArray) {                // All typed array
      result = new obj.constructor (obj);
    } else if (obj instanceof RegExp) {                    // RegExp
      result = new (obj.constructor) (obj.source, obj.flags || obj.options);
    } else if (obj instanceof Error) {                     // Error and derived objects
      result = new (obj.constructor) (obj.toString ());
      if (obj.stack) {
        result.stack = obj.stack;                          // Firefox fix
      }
    } else if (hasNode && obj instanceof Node) {           // HTML Node
      result = obj.cloneNode (true);
    } else if (obj.constructor === Object) {               // Custom propotype
      result = Object.create(proto);
    } else {                                               // Array or other objects
      result = new (obj.constructor) ();
    }
    stack.set(obj, result);                                // Update stack for circular objects
    for (let key of Reflect.ownKeys (obj)) {               // Properties
      const descrOrigin = Object.getOwnPropertyDescriptor (obj, key);
      const descrResult = Object.getOwnPropertyDescriptor (result, key);
      if (!descrResult || descrResult.configurable) {
        if (descrOrigin.set || descrOrigin.set) {          // descriptor with getter and setter
          Object.defineProperty ( result, key,
            Object.assign (descrOrigin, {
              set : (descrOrigin.set ? descrOrigin.set.bind (result) : undefined),
              get : (descrOrigin.get ? descrOrigin.get.bind (result) : undefined)
            })
          );
          result[ key ] = copy (obj[ key ]);
        } else {                                           // descriptor with value
          Object.defineProperty ( result, key,
            Object.assign (descrOrigin, {value : copy (obj[ key ])})
          );
        }
      }
    }
    return result;
  }
  
  copy.symbol = Symbol ('method copy');

  return copy;
  
})();

Son unas pocas l铆neas de c贸digo, y seguramente se explican por si solas, pero vamos a explicar que estamos haciendo paso a paso:

  • Se crea una constante copy con el retorno de una funci贸n de ejecuci贸n inmediata.
  • Se crean unas constantes:
    • Obtenemos el constructor de Int32Array para poder identificar todos los Typed Array
    • Comprobamos si Node est谩 disponible (s贸lo lo estar谩 en los navegadores)
    • Comprobamos si SharedArrayBuffer est谩 disponible, ya no todas las implementaciones de Javascript lo han incorporado
    • Creamos una lista de descriptores de objetos que vamos a ignorar.
    • Creamos un objeto Map donde vamos a guardar los objetos que ya hemos procesado para gestionar adecuadamente las referencias circulares.
  • Se define la funci贸n copy:
    • Si no es un objeto, entonces ser铆a un valor primitivo o una funci贸n, se devuelve el valor original
    • Si es null o uno de los tipos que no se van a copiar, se devuelve el valor original
    • Si ya ha sido proceso est谩 dentro del objeto Map y para evitar procesarlo en bucle infinito devolvemos el objeto que se incluy贸 en esa pila
    • Si el objeto tiene un m茅todo definido con copy.simbol, se llama a ese m茅todo para obtener una copia del objeto
    • Si el prototipo del objeto es el mismo que Object, entonces es un objeto simple que podemos crear con el literal {}
    • Si el prototipo del objeto es null, entonces el objeto se cre贸 con Object.create(null)
    • Si el objeto est谩 heredando de Date, Boolean, String o Number, se llama a su constructor pasando el valor interno obtenido con .valueOf()
    • Si el objeto est谩 heredando de Map, se llama al constructor y se insertan los valores con el m茅todo .set()
    • Si el objeto est谩 heredando de Set, se llama al constructor y se insertan los valores con el m茅todo .add()
    • Si el objeto est谩 heredando de ArrayBuffer o SharedArrayBuffer se obtiene una copia por medio del m茅todo .slice()
    • Si el objeto est谩 heredando de DataView, se llama al constructor pasando como par谩metro una copia del buffer que est谩 manejando
    • Si el objeto est谩 heredando de TypeArray, se llama al constructor pasando como par谩metro el propio objeto
    • Si el objeto est谩 heredando de RegExp, se llama al constructor pasando la expresi贸n regular obtenido de la propiedad .source y los modificadores por medio de la propiedad .flags u .optionsya que dependiendo de la implementaci贸n est谩 en una propiedad o en otra
    • Si el objeto est谩 heredando de Error, se llama al constructor pasando el mensaje como par谩metro y copiando la propiedad .stack en Firefox
    • Si el objeto est谩 heredando de Node, estamos en un navegador y copiamos el elemento con el m茅todo .cloneNode()
    • Si el constructor es Object, pero hemos llegado hasta aqu铆, quiere decir que el prototipo no es el del Object, por lo que es el caso especial de creaci贸n con Object.create({ })
    • En cualquier otro caso, es un Array o un objeto creado a partir de una clase
    • Para todos los objetos, se recorren sus propiedades propias, enumerables y no enumerables, y
      • Se obtiene el descriptor de la propiedad para el objeto original y el que hemos creado
      • Si en el objeto creado se permite la modificaci贸n de la propiedad
        • Si el descriptor es de tipo setter/getter
          • se crea con esa estructura en el objeto
          • se asigna el valor llamando de forma recursiva a copy()
        • Si el descriptor es de tipo value, se llama asigna el valor聽de forma recursiva a copy()
    • Se retorna el nuevo objeto
  • Creamos un Symbol para poder utilizarlo como nombre del m茅todo de copia en nuestras clases.
  • Se retorna la funci贸n copy

Conclusiones

Cuando empezamos parec铆a que todo ser铆a muy sencillo, que un operador y algunas l铆neas en una peque帽a funci贸n nos iban a resolver todos nuestros problemas a la hora de copiar objetos. Lo cierto es que en muchos casos esta aproximaci贸n simplificada cubre la mayor铆a de las situaciones y puede ser razonable su uso. Intencionadamente hemos querido empezar de esta forma, c贸mo suelen iniciarse el desarrollo de este tipo de funciones, partiendo de unos pocos casos sencillos.

En la mayor铆a de las ocasiones vamos a darnos cuenta r谩pidamente que tenemos que dar respuesta a m谩s situaciones de las que hab铆amos pensado inicialmente, especialmente si lo que estamos es buscando una funci贸n verdaderamente robusta, que de respuesta a todos los casos. El Javascript moderno ofrece una buena cantidad de tipos de objetos y algunos casos bastante peculiares que hacen que los objetos se comporten de forma diferente. No es demasiado complicado dar respuesta a todos ellos. Quiz谩s pueda resultar un poco tedioso analizar caso por caso, pero existen respuestas para todos los casos, s贸lo tenemos que aplicarnos un poco para ser exhaustivos.

Si vais a abordar alguna funci贸n global para el manejo de objetos os recomendamos que teng谩is a mano una lista de objetos posibles en Javascript y comprob茅is como funciona vuestro c贸digo en cada caso. Os podr茅is sorprender en bastantes situaciones de lo poco preparados que estamos para responder a a un simple objeto Date,聽 a un objeto Map o a una referencia circular. No hay que agobiarse, s贸lo tener en cuenta que el lenguaje ha crecido y tiene un mayor n煤mero de tipos de objetos a los que nos tenemos que enfrentar. Con un poco de pr谩ctica conseguiremos dar respuesta general a todos los casos que se nos presenten.

 

Novedades

HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)

HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)

En esta charla, organizada por MadridJS, Pablo Almunia nos muestra c贸mo la mayor铆a de nosotros cuando o铆mos hablar por primera vez de HTTP2 nos ilusionamos con las posibilidades que presumiblemente se abr铆an para el desarrollo de soluciones web avanzadas y c贸mo muchos nos sentimos defraudados con lo que realmente se pod铆a implementar.

En esta charla podemos ver c贸mo funciona el HTTP2, que debemos tener en cuenta en el servidor para hace uso de este protocolo y, sobre todo, c贸mo podemos enviar informaci贸n desde el servidor al cliente de forma efectiva y f谩cil. Veremos con detenimiento c贸mo por medio de los Server-Sent Events (SSE) podemos recibir en el cliente datos enviados desde el servidor sin utilizar websocket, simplificando enormemente la construcci贸n de aplicaciones con comunicaci贸n bidireccional.

Observables en Javascript con Proxies

Observables en Javascript con Proxies

En esta charla, organizada por MadridJS, Pablo Almunia nos habla de la observaci贸n reactiva de objetos en Javascript por medio de Proxy. Se describe paso a paso c贸mo funcionan los Proxies y en que casos pueden ser nuestro mejor aliado. Veremos que no hay que tenerles miedo, son bastante sencillos de utilizar, y nos ofrecen una gran abanico de posibilidades.

Aplicaciones JAMStack, SEO friendly y escalables con NextJS

Aplicaciones JAMStack, SEO friendly y escalables con NextJS

En esta charla de Madrid JS, Rafael Ventura nos describe las funcionalidades clave de NextJS, nos muestra en vivo c贸mo desarrollar una completa aplicaci贸n JAMStack con Server Side Rendering (SSR) y Static Site Generation (SSG) y termina mostrando como publicar esta aplicaci贸n en Vercel.

Stencil JS: mejora el Time To Market de tu producto, por Rub茅n Aguilera

Stencil JS: mejora el Time To Market de tu producto, por Rub茅n Aguilera

En esta charla Rub茅n Aguilera nos cuenta los problemas que tienen muchas empresas a la hora de sacar productos accesibles, vistosos y usables en el Time To Market que requiere Negocio y c贸mo podemos minimizar este tiempo gracias al DevUI con StencilJS para adecuar una aplicaci贸n de Angular a las exigencias del mercado en tiempo record.