Seleccionar página

Breve introducción a la inmutabilidad

El desarrollo con datos inmutables es un principio fundamental de la programación funcional que se está extendiendo también en la programación orientada a objeto. Su planteamiento básico es muy sencillo: un dato u objeto, una vez creado, no puede ser cambiado, manteniendo su estado original en todo momento. Si por algún motivo se tuviera que cambiar el dato, entonces se obtendría una copia con los datos modificados, pero nunca se cambian los valores originales.

Lo contrario a la inmutabilidad es la mutabilidad, es decir, la capacidad para cambiar el valor o el estado de los elementos de un lenguaje de programación. Cuando cambiamos el valor de una propiedad, o la referencia de una variable, estamos haciendo uso la mutabilidad, es decir, de la capacidad de cambiar.

El uso de estructuras mutables es una de las razonas esgrimidas para el déficit de confianza en muchos lenguajes, incluido Javascript. Este es un lenguaje que permite una gran capacidad de mutación de sus elementos, prácticamente cualquier dato puede transformarse en algo diferente en un momento determinado.

Se suele indicar que la mutación es la principal causa de los efectos secundarios, es decir, cuando varias partes del código cambian un mismo dato, es muy probable que se produzcan errores, ya que determinado código espera encontrarse con unos determinados datos y se encuentra con otros bien diferentes, ya que han sido cambiado en otra parte de nuestra aplicación.

También se argumenta que la mutación es una causa de los estados impredecibles en los datos y objetos, lo que exige una programación defensiva, comprobando que los datos son del tipo que esperamos, están completos o los objetos siguen en el mismo estado interno y no han sido cambiados después de haber sido pasados a otra función.

Se completa esta crítica indicando que la mutación requiere una depuración compleja en tiempo de ejecución. Cuando se analiza un error hay trazar el código intentado descubrir qué es lo que se cambió inesperadamente, que contenido tienen las variables en cada punto de ejecución del programa, dónde se realizaron los cambios de estado o de valores y por qué unas veces lo encontramos de una forma y otras veces de otra.

La inmutabilidad no significa que no haya cambios. Esto no sería realmente razonable, las cosas cambian, los usuarios modifican los valores y los estados, pero cualquier modificación debe reflejarse creando un nuevo estado y manteniendo el original sin cambios. Es decir, se genera es una nueva versión del estado que refleje el cambio, se obtiene un nuevo objeto con el cambio aplicado, pero no se modifica el objeto original.

Immutabilidad en Javascript

Aunque Javascript es extremadamente flexible y tiene una gran capacidad de mutación, tenemos un ejemplo bastante sencillo de inmutabilidad, que facilita mucho la comprensión de este concepto: las cadenas de texto son siempre inmutables. Si intentamos modificar una cadena, veremos que realmente no es posible, cómo mucho obtendremos otra cadena con diferentes valor.

En modo no estricto podemos intentar cambiar un carácter de nuestra cadena, pero no lo conseguiremos, el intento de cambio falla silenciosamente y no se consigue cambiar la cadena:

let x = "hola mundo";
console.log(x); // hola mundo
x[0] = 'H';
console.log(x); // hola mundo

En modo estricto esta misma operación lanzará un error:

"use strict";
let x = "hola mundo";
console.log(x); // hola mundo
try {
  x[0] = 'H';
} catch(err) {
  console.error(err.message); // Cannot assign to read only property '0' of string 'hola mundo'
}
console.log(x); // hola mundo

Cuando creemos modificar una cadena, realmente estamos obteniendo otra, manteniendo la primera intacta, ya que ningún método de manipulación de string modifica la cadena de texto original:

let x = "hola mundo";
let y = x.replace('h', 'H');
console.log(x); // hola mundo
console.log(y); // Hola mundo
console.assert(x !== y);

Esto, que es muy fácil de comprender en las cadenas, es igual en todos los tipos básicos de Javascript. En realidad, no podemos cambiar un valor numérico o un valor booleano, lo que estaremos haciendo como mucho es sustituir uno por otro, pero el valor como tal es inmutable.

Por otro lado, la aparición de la instrucción const ha incorporado en Javascript una forma sencilla de establecer una variable como inmutable, es decir, que no puede cambiarse el valor que contiene.

var a = 1;
a = 2;
let b = 1;
b = 2;
const c = 1;
c = 2; // Throw error "Assignment to constant variable."

Lo que probablemente más confunde en Javascript es la forma en la que los objetos se referencian en las variables, ya que no es el objeto como tal el que está contenido en la variable, si no una referencia (en forma de puntero) es. En este caso la instrucción const no bloquea los cambios en el objeto, sólo bloquea los cambios entre una referencia y otra.

const obj = {a: 1};

obj.a = 2; // OK

obj = {a:3}; // KO: Throw error "Assignment to constant variable."

Objetos inmutables en Javacript: principios básicos

Vamos a ver cómo podemos hacer que los objetos sean también inmutables en Javascript. Afortunadamente el lenguaje nos da varios mecanismos para conseguir nuestro objetivo, mostrando una vez más su enorme flexibilidad.

Aproximación vía Proxy

Una primera aproximación es a la obtención de una versión inmutable de un objeto por medio de un Proxy que intercepte las acciones defineProperty y deleteProperty. Con estas dos capturas no es necesario que también capturemos la acción set, ya que esta, internamente, llama a defineProperty.

function immutableObject (obj) {
  return new Proxy (obj, {
    defineProperty (target, p, attributes) {
      throw new TypeError (`Cannot assign to read only property '${ p }' of object '#<Object>'`)
    },
    deleteProperty (target, p) {
      throw new TypeError (`Cannot delete property '${ p }' of object #<Object>`)
    }
  });
}

Si pasamos un objeto por esta función, ya no podremos modificar sus propiedades sin que se lance un error:

const obj = immutableObject({a: 1});
obj.a = 2; // Error: Cannot assign to read only property 'a' of object '#<Object>'

El primer problema que tenemos con es que esta protección sólo se hace en el primer nivel del objeto, es decir, en las propiedades definidos directamente en él, y no en propiedades que puedan contenerse en otros niveles más profundos. Es lo que se suele llamar cambio superficial, y nosotros vamos a necesitar un cambio en profundidad. Para ello podemos ajustar nuestra función de la siguiente forma:

function immutableObject (obj) {
  return typeof obj !== 'object' ?
    obj :
    console.log (Object.values (obj)) ||
    Object.keys (obj).forEach (prop => obj[ prop ] = immutableObject (obj[ prop ])) ||
    new Proxy (obj, {
      defineProperty (target, p, attributes) {
        throw new TypeError (`Cannot assign to read only property '${ p }' of object '#<Object>'`)
      },
      deleteProperty (target, p) {
        throw new TypeError (`Cannot delete property '${ p }' of object #<Object>`)
      }
    })
}

 

Aunque esta es una aproximación válida, lo cierto es que puede resultar un poco engorrosa, ya que el trabajo con Proxys no siempre resulta cómodo. Veamos una alternativa:

Object.freeze()

El método Object.freeze() congela el objeto e impide que cambiemos sus propiedades o las borremos. Como suele ser habitual en Javascript, este cambio se hace de forma superficial y no afecta a los objetos que se contengan dentro de las propiedades. Para resolver esta limitación podemos crear una función que aplique de forma recursiva Object.freeze():

const immutableObject = (obj) =>
  typeof obj === 'object' ?
    Object.values (obj).forEach (immutableObject) || Object.freeze (obj) :
    obj;

Ahora, cualquier cambio en las propiedades del objeto lanzará un error en modo estricto y será ignorada en modo no estricto. Esta diferencia es la misma que vimos anteriormente cuando intentábamos cambiar una cadena de texto.

const obj = immutableObject({p: {a: 1}});
obj.p.a = 2;  // Error: Cannot assign to read only property 'a' of object '#<Object>'

Con esta aproximación se consigue una bastante completa gestión de objetos inmutables, pero no para todos los tipos de objetos. Vamos a ver por qué…

Objetos inmutables: una función para congelarlos a todos

Como ya hemos visto en varias ocasiones, Javascript dispone de una importante cantidad de tipos de objetos, no sólo los literales definidos con llaves { }, también son objetos los Array, Date, Map, Set, ArrayBuffer, etc. Tenemos que encontrar una forma de poder congelar en profundidad el estado de todos estos objetos.

Para abordar esta función, en primer lugar, vamos a ver que pasa con un sencillo objeto, por ejemplo, una fecha. Un objeto de tipo Date(). Si lo bloqueamos con Object.freeze() no podremos añadir nuevas propiedades al objeto, pero sí podremos cambiar su valor interno, por ejemplo con .setFullYear().

const d = new Date('2019-01-01T00:00:00.000Z');

Object.freeze(d);

console.log('antes', d);

d.setFullYear(d.getFullYear() - 100);

console.log('después', d);

Para poder congelar de forma efectiva un objeto Date deberíamos eliminar la posibilidad de cambiar su estado interno con todos los métodos que empiezan con set…(). Esto lo podríamos hacer por medio de un Proxy. Cuando se soliciten los métodos mutadores, podemos devolver un error o crea una nueva versión de estos métodos que devuelvan una nueva fecha sin modificar la actual.

En el caso de los objetos Array hay bastantes métodos, por ejemplo como .sort(), que modifican completamente la estructura de la matriz. En el caso de Array, si hemos utilizado Object.freeze() sí obtendremos un error al intentar ejecutar un método que modifica su contenido, ya que este cambio se ve reflejado en las propiedades del objeto y estas han sido congeladas.

const a = ['a', 'c', 'e', 'f', 'b', 'd'];

Object.freeze(a);

a.sort(); // Cannot assign to read only property '0' of object '[object Array]'

Aunque hemos conseguido bloquear el objeto, quizás sería interesante poder aplicar una técnica similar a la que hemos descrito antes con Date y hacer que estos métodos que mutan el objeto devuelvan un nuevo objeto, dejando el primero sin cambios. Esto se puede aplicar a Array, Map, Set, etc.

Por último debemos revisar que objetos de los que podemos crear en Javascript no tiene mucho sentido que los bloqueemos. Por ejemplo, no tiene mucho sentido que intentemos bloquear el estado de un objeto tipo Promise o de un iterador. Su propia naturaleza exige que su estado interno se pueda cambiar. También tenemos algunos objetos como WeakMap o WeakSet que por su propio funcionamiento es imposible que consigamos bloquerarlos ya que las referencias que contienen son eliminadas de forma automática por el recolector de basura de Javascript.

Aunque en proceso de estandarización hubo una cierta discusión sobre si se podría o no aplicar Object.freeze() a los Typed Array y finalmente en la especificación se indica que se debe producir un error si se intentan hacer inmutables cualquier objeto que hereda de TypedArray. Lo cierto es que aplicando nuestra técnica de reescritura de los métodos mutadores podemos crear objetos del tipo TypedArray inmutables que devuelvan nuevos ArrayBuffer cuando se intenten hacer cambios sobre los valores originales, aunque no podamos aplicar Object.freeze() sobre ellos.

Utilidad Immutable

Para poder gestionar con facilidad la inmutabilidad de todos los tipos de objetos de Javascript y que de forma razonable podemos crear como inmutables, hemos creado una pequeña utilidad que convierte cualquier objeto en Immutable y que consta de las siguientes funciones:

  • Immutable( object ) o Immutable.create( object ): devuelve un objeto inmutable en profundidad (todos los objetos contenidos en el objeto también son inmutables). Si el objeto tiene métodos que devuelven nuevos objetos o que intentan modificar el objeto, entonces -si hemos sido capaces de identificarlos- los cambiamos por métodos que devuelven una copia inmutable del objeto con el cambio.
  • Immutable.assign(target, origin): realiza aplica las propiedades del objeto origin en en el objeto target, devolviendo una copia inmutable y manteniendo target sin cambios. Este cambio se produce en profundidad, es decir, a cualquier nivel del objeto y no sólo a nivel superficial.
  • Immutable.isImmutable(object) devuelve true si el objeto es inmutable.

El código fuente de esta utilidad es bastante pequeño y lo recogemos aquí con dos versiones, una con estilo funcional y otra con estilo imperativo (que cada uno lea y utilice la que considere más adecuada):

 

const Immutable = (() => {
  
  const ISIMMUTABLE = Symbol ('immutable object');
  const refs        = new WeakMap ();
  const ignoreTypes = [ 'ArrayBuffer',  'Array Iterator', 'AsyncGenerator', 'Generator',
    'String Iterator', 'Async Iterator', 'Map Iterator', 'Set Iterator',
    'RegExp String Iterator', 'Promise', 'SharedArrayBuffer', 'WeakMap', 'WeakSet' ];
  const TypedArray  = Object.getPrototypeOf (Int32Array);
  
  const handler = (mutators, constructor) =>
    (target, prop, receiver) =>
      prop === ISIMMUTABLE ?
        true :
        mutators && mutators.includes (prop) ?
          ((result) =>
              (...args) =>
                result[ prop ] (...args) || true ?
                  Immutable (result) :
                  void (0)
          ) (constructor (target, prop, receiver)) :
          ((result) =>
              (typeof result === 'function' && prop !== 'constructor') ?
                (...args) => Immutable (result.bind (target) (...args)) :
                result
          ) (Reflect.get (target, prop));
  
  const Immutable = (obj) =>
    ((typeof obj !== 'object' && typeof obj !== 'function') ||
     obj === null ||
     ignoreTypes.includes (obj[ Symbol.toStringTag ]) ||
     Object.isFrozen (obj)) ?
      obj :
      (refs.has (obj)) ?
        refs.get (obj) :
        ((result) =>
          refs.set (obj, result) &&
          (Reflect.ownKeys (obj)
                 .forEach((prop) =>
            result[ prop ] = Immutable (obj[ prop ])) ||
          !(obj instanceof TypedArray) ? Object.freeze (result) : result)
        )(
          (obj instanceof Date) ?
            new Proxy (obj, {
              get : handler (
                [ 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 'setMinutes',
                  'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
                  'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth',
                  'setUTCSeconds', 'setYear' ],
                (target, prop, receiver) => new target.constructor (target.valueOf ())
              )
            }) :
            (obj instanceof Array) ?
              new Proxy (obj, {
                get : handler (
                  [ 'pop', 'push', 'shift', 'unshift', 'splice', 'copyWithin', 'fill',
                    'sort', 'reverse' ],
                  (target, prop, receiver) => new target.constructor (...target)
                )
              }) :
              (obj instanceof Map) ?
                new Proxy (
                  [ ...obj.keys () ]
                    .reduce (
                      (map, key) => key ?
                        obj.set (key, Immutable (obj.get (key))) :
                        void (0),
                      obj), {
                    get : handler (
                      [ 'clear', 'delete', 'set' ],
                      (target, prop, receiver) => new target.constructor (target)
                    )
                  }) :
                (obj instanceof Set) ?
                  new Proxy (obj, {
                    get : handler (
                      [ 'add', 'clear', 'delete' ],
                      (target) => new target.constructor (obj)
                    )
                  }) :
                  (obj instanceof DataView) ?
                    new Proxy (obj, {
                      get : handler (
                        [ 'setBigInt64', 'setBigUint64', 'setFloat32',
                          'setFloat64', 'setInt16', 'setInt32',
                          'setInt8', 'setUint16', 'setUint32', 'setUint8' ],
                        (target) => new target.constructor (target.buffer.slice (0))
                      )
                    }) :
                    (obj instanceof TypedArray) ?
                      new Proxy (obj, {
                        get : handler (
                          [ 'copyWithin', 'fill', 'reverse', 'set', 'sort' ],
                          (target) => new target.constructor (target)
                        )
                      }) :
                      Object.defineProperty (
                        obj,
                        ISIMMUTABLE,
                        {enumerable : false, value : true}
                      )
        );
  
  const assign = (obj, newData) =>
    ((result) => {
      Reflect.ownKeys (newData)
             .filter (key => obj[ key ] !== newData[ key ])
             .forEach (key => result[ key ] =
               (typeof newData[ key ] !== 'object' ||
                Reflect.ownKeys (newData[ key ]).length === 0) ?
                 newData[ key ] :
                 assign (obj[ key ], newData[ key ]));
      Reflect.ownKeys (obj)
             .filter (key => typeof result[ key ] === 'undefined')
             .forEach (key => result[ key ] = obj[ key ]);
      return Immutable (result);
    }) (((proto) =>
        proto === null ? Object.create (null) :
          obj.constructor !== Object ? new obj.constructor () :
            proto !== Object.prototype ? Object.create (proto) :
              obj.constructor ()
    ) (Object.getPrototypeOf (obj)));
  
  const isImmutable = (obj) => obj[ ISIMMUTABLE ] ||
                               (typeof obj !== 'object' && typeof obj !== 'function');
  
  Immutable.assign      = assign;
  Immutable.create      = Immutable;
  Immutable.isImmutable = isImmutable;
  return Object.freeze (Immutable);
}) ();
const Immutable = (() => {
  
  const ISIMMUTABLE = Symbol ('immutable object');
  const refs        = new WeakMap ();
  const ignoreTypes = [ 'ArrayBuffer', 'Array Iterator', 'AsyncGenerator', 'Generator',
    'String Iterator', 'Async Iterator', 'Map Iterator', 'Set Iterator',
    'RegExp String Iterator', 'Promise', 'SharedArrayBuffer', 'WeakMap', 'WeakSet' ];
  const TypedArray  = Object.getPrototypeOf (Int32Array);
  
  function handler (mutators, constructor) {
    return function (target, prop, receiver) {
      if (prop === ISIMMUTABLE) {
        return true;
      }
      if (mutators && mutators.includes (prop)) {
        return function (...args) {
          const result = constructor (target, prop, receiver);
          result[ prop ] (...args);
          return Immutable (result);
        };
      }
      const result = Reflect.get (target, prop);
      if (typeof result === 'function' && prop !== 'constructor') {
        return function (...args) {
          return Immutable (result.bind (target) (...args));
        }
      }
      return result;
    };
  }
  
  function Immutable (obj) {
    if ((typeof obj !== 'object' && typeof obj !== 'function') ||
        obj === null ||
        ignoreTypes.includes (obj[ Symbol.toStringTag ]) ||
        Object.isFrozen (obj))
    {
      return obj;
    }
    if (refs.has (obj)) {
      return refs.get (obj);
    }
    let result;
    if (obj instanceof Date) {
      result = new Proxy (obj, {
        get : handler (
          [ 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 'setMinutes',
            'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
            'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth',
            'setUTCSeconds', 'setYear' ],
          (target, prop, receiver) => new target.constructor (target.valueOf ())
        )
      });
    } else if (obj instanceof Array) {
      result = new Proxy (obj, {
        get : handler (
          [ 'pop', 'push', 'shift', 'unshift', 'splice', 'copyWithin', 'fill',
            'sort', 'reverse' ],
          (target, prop, receiver) => new target.constructor (...target)
        )
      });
    } else if (obj instanceof Map) {
      for (let key of obj.keys ()) {
        obj.set (key, Immutable (obj.get (key)));
      }
      result = new Proxy (obj, {
        get : handler (
          [ 'clear', 'delete', 'set' ],
          (target, prop, receiver) => new target.constructor (target)
        )
      });
    } else if (obj instanceof Set) {
      result = new Proxy (obj, {
        get : handler (
          [ 'add', 'clear', 'delete' ],
          (target) => new obj.constructor (obj)
        )
      });
    } else if (obj instanceof DataView) {
      result = new Proxy (obj, {
        get : handler (
          [ 'setBigInt64', 'setBigUint64', 'setFloat32',
            'setFloat64', 'setInt16', 'setInt32',
            'setInt8', 'setUint16', 'setUint32', 'setUint8' ],
          (target, prop, receiver) => new target.constructor (target.buffer.slice (0))
        )
      });
    } else if (obj instanceof TypedArray) {
      result = new Proxy (obj, {
        get : handler (
          [ 'copyWithin', 'fill', 'reverse', 'set', 'sort' ],
          (target, prop, receiver) => new target.constructor (target)
        )
      });
    } else {
      result = obj;
      Object.defineProperty (
        result,
        ISIMMUTABLE,
        {enumerable : false, value : true}
      );
    }
    refs.set (obj, result);
    for (let prop of Reflect.ownKeys (obj)) {
      result[ prop ] = Immutable (obj[ prop ]);
    }
    if (!(obj instanceof TypedArray)) {
      Object.freeze (result);
    }
    return result;
  }
  
  function assign (obj, newData) {
    let result;
    const proto = Object.getPrototypeOf (obj);
    if (proto === null) {
      result = Object.create (null)
    } else if (obj.constructor !== Object) {
      result = new obj.constructor ()
    } else if (proto !== Object.prototype) {
      result = Object.create (proto);
    } else {
      result = obj.constructor ();
    }
    for (key of Reflect.ownKeys (newData)) {
      if (obj[ key ] !== newData[ key ]) {
        if (typeof newData[ key ] !== 'object' ||
            Reflect.ownKeys (newData[ key ]).length === 0)
        {
          result[ key ] = newData[ key ];
        } else {
          result[ key ] = assign (obj[ key ], newData[ key ]);
        }
      }
    }
    for (let key of Reflect.ownKeys (obj)) {
      if (typeof result[ key ] === 'undefined') {
        result[ key ] = obj[ key ];
      }
    }
    return Immutable (result);
  }
  
  function isImmutable (obj) {
    return obj[ ISIMMUTABLE ] ||
           (typeof obj !== 'object' && typeof obj !== 'function');
  }
  
  Immutable.assign      = assign;
  Immutable.create      = Immutable;
  Immutable.isImmutable = isImmutable;
  return Object.freeze (Immutable);
}) ();

Librerías para estructuras de datos inmutables

Además de esta pequeña utilidad que aquí os mostramos, hay librerías muy completas y bastante populares para gestionar datos inmutables en Javascript. Dos de ellas tienen especial interés:

  • Ramda es una librería que permite gestionar tuberías de ejecución con datos inmutables. Es una librería bastante completa y con una amplia difusión en la comunidad.
  • Immutable es una librería desarrollada por Facebook para gestionar estructuras de datos inmutables de forma muy optimizada, ya que por medio de técnicas de hash maps tries y vector tries evitan tener que copiar grandes cantidades de datos cada vez que creamos una nueva versión de las colecciones.

Os animamos a que descubráis que os pueden aportar estas librerías o a desarrollar vuestras utilidades. Es bastante más sencillo de lo que puede parecer a simple vista.

Conclusiones

Sea cual sea el mecanismo que utilicemos, desde una mera convención en el equipo de desarrollo para no desarrollar o utilizar funciones que muten los datos, pasando por el bloqueo de cambios en los objetos por medio Object.freeze(), hasta las completas librerías de manejo de datos inmutables, es importante comprender y gestionar adecuadamente la inmutabilidad para obtener un código mucho más predecible, fácilmente depurable y, en general, más confiable.

Quizás pensemos que la inmutabilidad sólo es útil en programación funcional, pero no es así, la inmutabilidad se puede utilizar en cualquier estilo/modelo de programación. Quizás la extensa mutabilidad que ofrece Javascript nos haga pensar que es más idiomático utilizar la mutabilidad, pero Javascript también ofrece mecanismos y soluciones para gestionar de forma bastante sencilla la inmutabilidad. Usemos o no los mecanismos que nos ofrece el lenguaje para asegurar la inmutabilidad de los datos, es conveniente conocer como trabajar con este tipo de restricción y decidir luego cuando y cómo la utilizamos.

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.

Svelte JS: por qué dedicarle tiempo, por Jesús Cuesta

Svelte JS: por qué dedicarle tiempo, por Jesús Cuesta

Jesús Cuesta cuenta qué es Svelte, para qué sirve, cómo compite contra aplicaciones construidas con React o Vuejs, si sirve para desarrollar web components, si la curva de aprendizaje es muy alta, y sobre todo si está suficiente maduro para utilizarlo. Si quieres conocer Svelte no puedes perderte esta introducción.

Bye Bye Nativo, Welcome a la Web del Futuro por Alex González

Bye Bye Nativo, Welcome a la Web del Futuro por Alex González

Alex García nos contará como en breve la web ha tomado el control de todas las plataformas de desarrollo. Ya casi no quedarán aplicaciones Nativas y todas las plataformas se moverán utilizando JavaScript… Este es posiblemente el panorama que podremos ver en un futuro no muy lejano. En esta charla nos contará las principales claves de esta transformación.