¿Alguna vez has usado una entrada de objeto y te has preguntado cómo funciona? ¡Es mucho más sencillo de lo que crees!
Aquí hay una implementación básica:
function objectEntries(obj) { const entries = []; for (const key in obj) { if (Object.hasOwn(obj, key)) { entries.push([key, obj[key]]); } } return entries; }
Sin embargo, este código no es lo suficientemente bueno. ¿Qué pasaría si el objeto fuera masivo? El rendimiento del enfoque basado en matrices deberá almacenarse en la memoria durante todo el proceso de ejecución de esta función. ¿Y si lo vuelves a usar? De todos modos, tendrá que crear y mantener una nueva matriz en la memoria. En el mundo real, esto puede provocar problemas graves de rendimiento y, en algún momento, tendríamos que adaptarnos al rendimiento. Sin embargo, existe una solución elegante que resolverá todos estos problemas, ¡en la que Symbol.iterator viene al rescate!
Aquí hay un fragmento actualizado:
function objectEntries(obj) { return { [Symbol.iterator]() { const keys = Object.keys(obj); let index = 0; return { next() { if (index¿Por qué utilizar Symbol.iterator para la iteración?
En nuestra implementación inicial, la función objectEntries crea una matriz de todas las entradas (pares [clave, valor]) en la memoria, lo que puede ser un problema si el objeto tiene una gran cantidad de propiedades. Almacenar todas las entradas en una matriz significa que tenemos que asignar memoria para cada par por adelantado. Este enfoque está bastante bien para objetos más pequeños, pero rápidamente se vuelve ineficiente y simplemente lento a medida que crece el tamaño del objeto.
En el código actualizado, definimos [Symbol.iterator] en un objeto que contiene la lógica de iteración. Analicémoslo paso a paso:
Aplicación de Symbol.iterator a bucles personalizados
Profundicemos en cómo estos métodos pueden proporcionar más control sobre el comportamiento del bucle. Cada uno de los ejemplos proporcionados demuestra una forma única de interactuar con datos de matriz, agregando mucha flexibilidad a su código. Exploraremos las implicaciones de cada método y cómo se pueden aprovechar en diferentes escenarios.
En estos ejemplos, voy a ampliar el prototipo de Array (más información sobre los prototipos aquí) con los métodos de ejemplo para que mi código sea más fácil de leer. ¡Vamos a empezar!
Por ejemplo, este método de iterador inverso puede resultar útil en algo como una aplicación de chat donde es posible que desees mostrar primero los mensajes más recientes. Las aplicaciones de chat son conocidas por tener MUCHOS datos (mensajes en este caso). Con ReverseIterator, puede iterar a través de una lista de mensajes y mostrarlos en el orden deseado sin necesidad de crear una nueva matriz invertida.
Array.prototype.reverseIterator = function() { let index = this.length - 1; return { [Symbol.iterator]: () => ({ next: () => { if (index >= 0) { return { value: this[index--], done: false }; } return { done: true }; } }) }; }; const numbers = [1, 2, 3, 4, 5]; for (const num of numbers.reverseIterator()) { console.log(num); // 5, 4, 3, 2, 1 }
Este método único le permite iterar a través de una matriz mientras se asegura de que solo se obtengan valores únicos. Esto es súper útil para eliminar duplicados sobre la marcha, sin filtrarlos por adelantado y usar más memoria.
Array.prototype.unique = function() { const seen = new Set(); return { [Symbol.iterator]: () => ({ next: () => { for (let i = 0; iEl método de fragmentos siguiente puede ser útil cuando se trata de conjuntos de datos grandes; puede procesarlos en fragmentos más pequeños para reducir el uso de memoria y mejorar el rendimiento. Digamos que está importando datos desde algo como un archivo CSV, puede leerlos y procesarlos en segmentos más escalables. Además, en las interfaces de usuario web, la fragmentación se puede utilizar para la paginación, lo que le permite mostrar una cantidad específica de elementos por página o ayudarlo a administrar mejor un cargador infinito.
Array.prototype.chunk = function(size) { let index = 0; return { [Symbol.iterator]: () => ({ next: () => { if (indexConclusión
En este artículo, exploramos cómo Symbol.iterator personaliza la lógica y mejora la eficiencia de nuestros bucles. Al implementar métodos iterables personalizados en Array.prototype (o cualquier otro iterable en ese sentido), podemos administrar el uso de la memoria de manera efectiva y controlar cómo se ejecuta nuestro bucle.
El ejemplo inicial de objectEntries demostró cómo un enfoque basado en matrices puede generar problemas de rendimiento al manejar objetos grandes. Sin embargo, al utilizar SYmbol.iterator, creamos una solución eficiente que nos permite iterar sobre entradas de objetos sin la sobrecarga de una asignación de memoria innecesaria.
También analizamos varios ejemplos prácticos de cómo extender Array.prototype puede facilitar diversos escenarios del mundo real con los que los desarrolladores tienen que lidiar en el día a día.
Con estas potentes herramientas a tu disposición, estarás mejor equipado para resolver escenarios complejos de manejo de datos en JavaScript con implicaciones de rendimiento cercanas a cero en tu aplicación.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3