Ecma Script 2023 [ES2023] Introduce nuevos métodos para hacer copias en vez de mutar Arrays en Javascript

Explora y comprende el funcionamiento de los nuevos métodos que posee el objeto Array en la versión ES2023 de Javascript


Introducción

Recientemente, ha finalizado la ECMASCRIPT 2023 Specification. En ella se incluyen nuevos métodos en el objeto Array que nos ayudarán a mejorar nuestro código Javascript, haciéndolo más mantenible y predecible.

Los métodos toSorted, toReversed, toSpliced y with nos permiten realizar operaciones en Arrays sin cambiar el Array original. Esto se logra, primero, creando un nuevo objeto que sea copia del original, y segundo, modificar el objeto copiado (el objeto original mantiene su estado). Lea más para saber las diferencias de estos nuevos métodos y cómo comenzar a utilizarlos en tus proyectos.

Nuevos metodos arrays Javascript 2023
Foto de Michael Dziedzic en Unsplash

Mutaciones y efectos secundarios (side effects)

El objeto Array siempre tuvo sus truquitos. Métodos como sort, reverse y splice mutan el objeto en el que se invoca la función. Otros métodos como concat, map y filter crean una copia del objeto y modifican este último. Cuando tú llevas a cabo una operación en la que el objeto original es mutado por sí mismo, a esto le llamamos un efecto secundario o en inglés “side effect”.

Los side effects pueden causar comportamientos inesperados en tu sistema, por lo que hay que tratar de minimizarlos lo mayor posible.

Como ejemplo, esto es lo que sucede cuando inviertes un array:

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = languages.reverse();
console.log(reversed);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
console.log(languages);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
console.log(Object.is(languages, reversed));
// => true

Como puedes observar, el arreglo original fue invertido (mutado) incluso si guardamos el resultado que nos arroja la invocación de reverse, en una nueva variable. Aquí, simplemente las variables languages y reversed apuntan al mismo objeto (de tipo Array).

Mutando Arrays & React/Vue

Unos de los problemas más comunes que tenemos a la hora de utilizar React y la mutación de arreglos, es que no puedes mutar un arreglo y tratar de asignar como nuevo estado a este. Esto se debe a que el arreglo sigue siendo el mismo objeto, por lo tanto, React no disparará un nuevo rénder.

En cambio, tú primero necesitarás copiar el arreglo, luego mutar la copia y finalmente setear la copia como nuevo estado de nuestro componente React. La documentación de React tiene una página dedicada a cómo actualizar arreglos que pertenecen a un estado.

Para los usuarios de Vue, esto es resuelto con el famoso atributo :key cuando utilizas v-for⁣: Mantener estado de las listas con key.

Primero copia, luego muta

La manera de solucionar esto es usar la regla: primero copiar y luego mutar. Existen diversas técnicas para copiar un objeto de tipo Array, entre ellas, Array.from, el operador spread, o invocar a la función slice sin argumentos.

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = Array.from(languages).reverse();
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
console.log(Object.is(languages, reversed));
// => false

Es genial tener una solución a este problema con respecto a la mutación, sin embargo, estas técnicas requieren de que el programador ejecute los métodos en una secuencia determinada. Esto podría dar lugar a ciertos errores, sobre todo para los desarrolladores que no poseen tanta experiencia con Javascript.

Los nuevos métodos: mutar objeto copiado

Aquí es donde los nuevos métodos juegan un papel importante. Los métodos toSorted, toSpliced, toReversed y with se encargan de copiar el array por ti, cambiarlo (o mutarlo) y retornarlo. Esto hará que tu código sea más fácil de leer y escribir.

A continuación se explicará qué hace cada método

Array.prototype.toSorted

La función toSorted retorna un nuevo arreglo ordenado.

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const sorted = languages.toSorted();
console.log(sorted);
// => [ 'CoffeeScript', 'JavaScript', 'TypeScript' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]

La forma en cómo se comporta toSorted a la hora de ordenar elementos es la misma que sorted. Debes prestar atención a la hora de ordenar numbers, y strings que contengan acentos. Asegúrate de utilizar una función de comparación(callback) en estos casos.

const numbers = [5, 3, 10, 7, 1];
const sorted = numbers.toSorted();
console.log(sorted);
// => [ 1, 10, 3, 5, 7 ]
const sortedCorrectly = numbers.toSorted((a, b) => a - b);
console.log(sortedCorrectly);
// => [ 1, 3, 5, 7, 10 ]
const strings = ["abc", "äbc", "def"];
const sorted = strings.toSorted();
console.log(sorted);
// => [ 'abc', 'def', 'äbc' ]
const sortedCorrectly = strings.toSorted((a, b) => a.localeCompare(b));
console.log(sortedCorrectly);
// => [ 'abc', 'äbc', 'def' ]

Array.prototype.toReversed

La función toReversed retorna un nuevo array ordenado de forma inversa.

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const reversed = languages.toReversed();
console.log(reversed);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]

Array.prototype.toSpliced

La función toSpliced, se comporta un tanto diferente que la clásica splice. El método splice muta el propio objeto array, lo hace añadiendo y eliminando elementos del mismo. Consiguientemente, esta función retorna los elementos eliminados del arreglo.

Por el otro lado, toSpliced retorna un arreglo que contiene cualquier elemento añadido (se descartan los elementos eliminados).

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const spliced = languages.toSpliced(2, 1, "Dart", "WebAssembly");
console.log(spliced);
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]

Si estás utilizado el valor retornado por splice, no es recomendable que utilices este nuevo método en su reemplazo (no son compatibles en la manera que se calcula su retorno).

Si deseas sabes los valores eliminados sin alterar el objeto original, entonces, deberías seguir utilizando la forma clásica de copia con splice(mencionada al principio).

Desafortunadamente, los métodos splice y slice toman diferentes argumentos. splice toma un índice y el número de elementos luego de este índice a ser eliminados. Por otro lado, slice toma dos índices: el inicio y el fin.

Si quieres utilizar toSpliced en reemplazado de splice y obtener los elementos eliminados, podrías utilizar toSpliced y slice en el arreglo original, algo así:

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const startDeletingAt = 2;
const deleteCount = 1;
const spliced = languages.toSpliced(startDeletingAt, deleteCount, "Dart", "WebAssembly");
const removed = languages.slice(startDeletingAt, startDeletingAt + deleteCount);
console.log(spliced);
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]
console.log(removed);
// => [ 'CoffeeScript' ]

Array.prototype.with

La función with equivale a usar la notación con corchetes para cambiar el valor de un elemento en el arreglo.

Entonces, en vez de modificar directamente el arreglo así:

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
languages[2] = "WebAssembly";
console.log(languages);
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]

Puedes copiar el arreglo y después modificarlo:

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const updated = languages.with(2, "WebAssembly");
console.log(updated);
// => [ 'JavaScript', 'TypeScript', 'WebAssembly' ]
console.log(languages);
// => [ 'JavaScript', 'TypeScript', CoffeeScript' ]

Los Arrays Tipados

Estos nuevos métodos no solo están implementados en el clásico objeto Array, sino que, además, los TypedArray también los incluyen. Esto significa que puedes utilizar toSorted, toReversed y with en los objetos Int8Array, BigUint64Array(desde el 8 hasta el 64). Los Arrays tipados no poseen el método splice, por lo que no existe el toSpliced.

Soporte

Es genial ver al estándar ECMAScript agregar nuevas funcionalidades que nos facilitan el trabajo a la hora de escribir nuestros sistemas en Javascript. El soporte por parte de los navegadores — al momento de escribir este artículo- es bastante bueno (ver soporte por navegador). En caso de que no encuentres compatibilidad con el navegador con el que estás trabajando, puedes utilizar pollyfills.


Finalizando

Estos nuevos métodos van a ser utilizados mayormente en el frontend -en mi opinión- dado a cómo funcionan internamente las tecnologías actuales (Reac, Vue, etc), particularmente cuando queramos reflejar un cambio de estado de un Array.

Espero que haya sido de tu utilidad, muchas gracias por leer este artículo.

Deseo que tengas un buen codeo, saludos.

Bibliografía: https://www.sonarsource.com/blog/es2023-new-array-copying-methods-javascript/