javascript reactjs immutable.js

javascript - ¿Por qué debería usar immutablejs sobre object.freeze?



reactjs immutable.js (6)

He investigado en la red sobre los beneficios de immutablejs sobre Object.freeze() pero no encontré nada satisfactorio!

Mi pregunta es ¿por qué debo usar esta biblioteca y trabajar con estructuras de datos no nativas cuando puedo congelar un objeto javascript antiguo?


Ambos no hacen que el objeto sea profundamente inmutable.

Sin embargo, utilizando Object.freeze , tendrá que crear las nuevas instancias del objeto / matriz por sí mismo, y no tendrán un intercambio estructural. Por lo tanto, cada cambio que requerirá una copia profunda de todo, y la antigua colección será recogida de basura.

immutablejs por otro lado, administrará las colecciones, y cuando algo cambie, la nueva instancia utilizará las partes de la instancia anterior que no han cambiado, por lo tanto, menos copia y recolección de basura.


Hay un par de diferencias importantes entre Object.freeze () y immutable.js.

Vamos a abordar el costo de rendimiento primero. Object.freeze () es superficial. Hará que el objeto sea inmutable, pero las propiedades y métodos anidados dentro de dicho objeto aún pueden ser mutados. La developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… aborda esto e incluso proporciona una función "deepFreeze", que es aún más costosa en términos de rendimiento. Immutable.js, por otro lado, hará que el objeto en su conjunto (propiedades anidadas, método, etc.) sea inmutable a un costo menor.

Además, si alguna vez necesita clonar una variable inmutable, Object.freeze () lo obligará a crear una variable completamente nueva, mientras que Immutable.js puede reutilizar la variable inmutable existente para crear el clon de manera más eficiente. Aquí hay una cita interesante sobre esto de este artículo :

"Los métodos inmutables como .set () pueden ser más eficientes que la clonación porque permiten que los nuevos datos de referencia del objeto en el objeto anterior: solo las propiedades cambiadas difieran. De esta manera, puede ahorrar memoria y rendimiento, en lugar de clonar todo constantemente".

En pocas palabras, Immutable.js hace conexiones lógicas entre las variables inmutables antiguas y nuevas, mejorando así el rendimiento de la clonación y el espacio que las variables congeladas toman en la memoria. Object.freeze () lamentablemente no: cada vez que clonas una nueva variable de un objeto congelado, básicamente escribes nuevamente todos los datos y no hay una conexión lógica entre las dos variables inmutables, incluso si (por alguna extraña razón) son idénticas datos.

Así que en términos de rendimiento, especialmente si utiliza constantemente variables inmutables en su programa, Immutable.js es una excelente opción. Sin embargo, el rendimiento no lo es todo y hay algunas advertencias importantes en el uso de Immutable.js. Immutable.js utiliza su propia estructura de datos, lo que hace que la depuración, o incluso el registro de datos en la consola, sea un verdadero problema. También puede conducir a una pérdida de la funcionalidad básica de JavaScript (por ejemplo, no puede usar la desestructuración de ES6 ). La documentación de Immutable.js es infame imposible de entender (porque originalmente fue escrita para su uso solo dentro de Facebook), lo que requiere una gran cantidad de búsquedas en la web, incluso cuando surgen problemas simples.

Espero que esto cubra los aspectos más importantes de ambos enfoques y lo ayude a decidir cuál funcionará mejor para usted.


La razón más importante que viene a la mente, aparte de tener una API funcional que ayuda con las actualizaciones inmutables, es el intercambio estructural utilizado por Immutable.js. Si tiene una aplicación que necesita inmutabilidad forzada (es decir, está usando Redux), si solo está usando Object.freeze, entonces estará haciendo una copia para cada "mutación". Esto no es realmente eficiente con el tiempo, ya que esto conducirá a la agudización de GC. Con Immutable.js, obtiene el intercambio estructural incorporado (en lugar de tener que implementar un conjunto de objetos / un modelo de intercambio estructural propio) ya que las estructuras de datos devueltas desde inmutable son Tries. Esto significa que todas las mutaciones aún están referenciadas dentro de la estructura de datos, por lo que la reducción de GC se mantiene al mínimo. Más sobre esto está en el sitio web de documentos de Immutable.js (y un gran video que profundiza en el creador, Lee Byron):

immutable.js


No creo que hayas entendido lo que te ofrece ImmutableJs. No es una biblioteca que solo convierte sus objetos en inmutables, es una biblioteca que trata de trabajar con valores inmutables.

Sin simplemente repetir sus docs y su declaración de misión , declararé dos cosas que proporciona:

  1. Los tipos Implementaron (inmutables) infinitos rangos, pilas, conjuntos ordenados, listas, ...

  2. Todos sus tipos se implementan como Estructuras de datos persistentes .

Mentí, aquí hay una cita de su declaración de misión:

Los datos inmutables no se pueden cambiar una vez creados, lo que lleva a un desarrollo de aplicaciones mucho más simple, sin copia defensiva, y habilita las técnicas avanzadas de memorización y detección de cambios con lógica simple. Los datos persistentes presentan una API mutativa que no actualiza los datos en el lugar, sino que siempre produce nuevos datos actualizados.

Les insto a que lean los artículos y videos a los que se vinculan y más sobre Estructuras de datos persistentes (ya que se trata de la cosa de la que se trata Immutablejs), pero resumiré en una oración más o menos:

Imaginemos que estás escribiendo un juego y tienes un jugador que se sienta en un plano 2d. Aquí, por ejemplo, está Bob:

var player = { name: ''Bob'', favouriteColor: ''moldy mustard'', x: 4, y: 10 };

Ya que bebiste el FP koolaid, quieres congelar al jugador (¡brrr! Espero que Bob tenga un suéter):

var player = Object.freeze({ name: ''Bob'', ... });

Y ahora entra en tu bucle de juego. En cada tick se cambia la posición del jugador. No podemos simplemente actualizar el objeto del reproductor ya que está congelado, por lo que lo copiamos de nuevo:

function movePlayer(player, newX, newY) { return Object.freeze(Object.assign({}, player, { x: newX, y: newY })); }

Eso está bien y excelente, pero note la cantidad de copias inútiles que estamos haciendo: en cada tic, creamos un nuevo objeto, iteramos sobre uno de nuestros objetos y luego asignamos algunos nuevos valores encima de ellos. En cada tick, en cada uno de tus objetos. Eso es todo un bocado.

Inmutable envuelve esto para ti:

var player = Immutable.Map({ name: ''Bob'', ... }); function movePlayer(player, newX, newY) { return player.set(''x'', newX).set(''y'', newY); }

Y a través de la ノ * ✧ ゚ magia ✧ ゚ * ヽ de estructuras de datos persistentes, prometen hacer la menor cantidad de operaciones posible.

También existe la diferencia de mentalidad. Cuando se trabaja con "un objeto javascript [simple] liso y viejo", las acciones predeterminadas de todo es asumir la mutabilidad, y usted debe hacer un esfuerzo adicional para lograr una inmutabilidad significativa (es decir, inmutabilidad que reconoce que el estado existe). Eso es parte de la razón por la que existe la freeze : cuando intentas hacer lo contrario, las cosas entran en pánico. Con Immutable, la inmutabilidad es, por supuesto, la suposición por defecto y tiene una buena API por encima.

Eso no quiere decir que todo es rosado y rosado con una cereza encima. Por supuesto, todo tiene sus inconvenientes, y no deberías meter Inmutable en todas partes solo porque puedes. A veces, simplemente freeze un objeto es suficiente. Diablos, la mayoría de las veces eso es más que suficiente. Es una biblioteca útil que tiene su nicho, simplemente no te dejes llevar por la exageración.


Según mis github.com/engineforce/ImmutableAssign , immutable.js está optimizado para operaciones de escritura , más rápido que Object.assign (), sin embargo, es más lento para operaciones de lectura. Por lo tanto, la decisión depende del tipo de su aplicación y su relación de lectura / escritura. A continuación se presenta el resumen de los resultados de los puntos de referencia:

-- Mutable Total elapsed = 103 ms = 50 ms (read) + 53 ms (write). -- Immutable (Object.assign) Total elapsed = 2199 ms = 50 ms (read) + 2149 ms (write). -- Immutable (immutable.js) Total elapsed = 1690 ms = 638 ms (read) + 1052 ms (write). -- Immutable (seamless-immutable) Total elapsed = 91333 ms = 31 ms (read) + 91302 ms (write). -- Immutable (immutable-assign (created by me)) Total elapsed = 2223 ms = 50 ms (read) + 2173 ms (write).

Idealmente, debe crear un perfil de su aplicación antes de introducir cualquier optimización de rendimiento, sin embargo, la inmutabilidad es una de las decisiones de diseño que deben decidirse antes. Cuando comienza a usar immutable.js , debe usarlo en toda la aplicación para obtener los beneficios de rendimiento, ya que interoperar con objetos JS simples usando fromJS () y toJS () es muy costoso.

PD: Acabo de descubrir que la matriz de congelación profunda (1000 elementos) se vuelve muy lenta para actualizar, aproximadamente 50 veces más lenta, por lo tanto, solo debe usar la congelación profunda en el modo de desarrollo. Resultados de los puntos de referencia:

-- Immutable (Object.assign) + deep freeze Total elapsed = 45903 ms = 96 ms (read) + 45807 ms (write).


Object.freeze no hace ninguna congelación de forma nativa, creo que immutable.js lo hace.

Lo mismo con cualquier biblioteca: por qué usar guiones bajos, jQuery, etc., etc.

A la gente le gusta reutilizar las ruedas que otras personas construyeron :-)