JavaScript: ¿qué sigue para nosotros el próximo año?





¡Buen dia amigos!



Este artículo se centra en las capacidades de JavaScript que se presentarán en la nueva versión de la especificación (ECMAScript 2021, ES12).



Se tratará de lo siguiente:



  • String.prototype.replaceAll ()
  • Promise.any ()
  • WeakRefs
  • Operadores de asignación booleanos
  • Separadores de números




String.prototype.replaceAll ()



String.prototype.replaceAll () ( cláusula Mathias Bynens ) le permite reemplazar todas las instancias de una subcadena en una cadena con un valor diferente sin usar una expresión regular global.



En el siguiente ejemplo, reemplazamos todos los caracteres "+" con comas con un espacio usando una expresión regular:



const strWithPlus = '++'
const strWithComma = strWithPlus.replace(/+/g, ', ')
// , , 


Este enfoque requiere el uso de una expresión regular. Sin embargo, las expresiones regulares complejas suelen ser una fuente de errores.



Hay otro enfoque basado en el uso de los métodos String.prototype.split () y Array.prototype.join ():



const strWithPlus = '++'
const strWithComma = strWithPlus.split('+').join(', ')
// , , 


Este enfoque evita el uso de expresiones regulares, pero debe dividir la cadena en partes separadas (palabras), convertirla en una matriz y luego concatenar los elementos de la matriz en una nueva cadena.



String.prototype.replaceAll () resuelve estos problemas y proporciona una forma simple y conveniente de reemplazar subcadenas globalmente:



const strWithPlus = '++'
const strWithComma = strWithPlus.replaceAll('+', ', ')
// , , 


Tenga en cuenta que para mantener la coherencia con las API anteriores, el comportamiento de String.prototype.replaceAll (searchValue, newValue) (searchValue es el valor de búsqueda, newValue es el nuevo valor) es el mismo que String.prototype.replace (searchValue, newValue), excepto por lo siguiente:



  • Si el valor que está buscando es una cadena, replaceAll reemplaza todas las coincidencias y reemplaza solo la primera
  • Si el valor deseado es una expresión regular no global, entonces replace reemplaza la primera coincidencia, y replaceAll arroja una excepción para evitar un conflicto entre la ausencia de la marca "g" y el nombre del método (reemplazar todo - reemplazar todas [coincidencias])


Si se usa una expresión regular global como valor de búsqueda, entonces replace y replaceAll se comportan igual.



¿Qué pasa si tenemos una línea con un número arbitrario de espacios al principio, al final de la línea y entre las palabras?



const whiteSpaceHell = '          '


Y queremos reemplazar dos o más espacios con uno. ¿Puede reemplazar todo resolver este problema? No.



Con String.prototype.trim () y reemplazar con una expresión regular global, esto se hace así:



const whiteSpaceNormal =
  whiteSpaceHell
    .trim()
    .replace(/\s{2,}/g, ' ')
    // \s{2,}     
    //   


Promise.any ()



Promise.any () (una sugerencia de Mathias Bynens, Kevin Gibbons y Sergey Rubanov ) devuelve el valor de la primera promesa cumplida. Rechazar todas las promesas pasadas a Promise.any () como argumento (como una matriz) arroja una excepción "AggregateError".



AggregateError es una nueva subclase Error que agrupa errores individuales. Cada instancia de AggregateError contiene una referencia a una matriz con excepciones.



Consideremos un ejemplo:



const promise1 = new Promise((resolve, reject) => {
  const timer = setTimeout(() => {
    resolve('p1')
    clearTimeout(timer)
  }, ~~(Math.random() * 100))
}) // ~~ -   Math.floor()

const promise2 = new Promise((resolve, reject) => {
  const timer = setTimeout(() => {
    resolve('p2')
    clearTimeout(timer)
  }, ~~(Math.random() * 100))
})

;(async() => {
  const result = await Promise.any([promise1, promise2])
  console.log(result) // p1  p2
})()


El resultado será el valor de la primera promesa resuelta.



Ejemplo de la oración:



Promise.any([
  fetch('https://v8.dev/').then(() => 'home'),
  fetch('https://v8.dev/blog').then(() => 'blog'),
  fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => {
  //  ()   
  console.log(first);
  // → 'home'
}).catch((error) => {
  //    
  console.log(error);
})




Tenga en cuenta que Promise.race (), a diferencia de Promise.any (), devuelve el valor de la primera promesa resuelta, ya sea cumplida o rechazada.



WeakRefs



WeakRefs (referencias débiles) ( propuesto por Dean Tribble, Mark Miller, Till Schneidereit, etc. ) proporciona dos características nuevas:



  • Crear referencias débiles a un objeto usando la clase WeakRef
  • Ejecución de finalizadores personalizados después de la recolección de elementos no utilizados con la clase FinalizationRegistry


En resumen, WeakRef le permite crear referencias débiles a objetos que son valores de propiedades de otro objeto, y los finalizadores pueden usarse, entre otras cosas, para eliminar referencias a objetos "limpiados" por el recolector de basura.



Esta técnica puede ser útil cuando se crea una función de memorización (memorización) que usa la memoria caché incorporada para evitar la ejecución repetida de la función si hay un valor calculado para el argumento pasado a la función en la memoria caché (siempre que los objetos se utilicen como valores para las propiedades del objeto de la memoria caché y el riesgo de su posterior eliminación) ...



Como recordará, el motivo de la aparición en JavaScript de una estructura como Map (tabla hash), además de la búsqueda más rápida de un valor por clave, fue que las claves de un objeto ordinario solo pueden ser cadenas o caracteres. Map, por otro lado, permite usar cualquier tipo de datos como clave, incluidos los objetos.



Sin embargo, pronto surgió un problema de fuga de memoria: eliminar los objetos que eran claves del mapa no los convertía en inaccesibles (marcar y barrer), lo que impedía que el recolector de basura los destruyera, liberando la memoria que ocupaban.



En otras palabras, los objetos utilizados como claves en el Mapa se guardan para siempre.



Se introdujo otra estructura, WeakMap (y WeakSet), para resolver este problema. La diferencia entre WeakMap y Map es que las referencias a objetos clave en WeakMap son débiles: eliminar tales objetos permite al recolector de basura reasignar la memoria asignada para ellos.



Así, esta propuesta representa la siguiente etapa en el desarrollo de una tabla hash en JavaScript. Los objetos ahora se pueden utilizar como claves y como valores en otros objetos sin riesgo de pérdida de memoria.



Una vez más, cuando se trata de construir una caché en línea:



  • Si no hay riesgo de pérdida de memoria, use Map
  • Cuando utilice objetos clave que puedan eliminarse posteriormente, utilice WeakMap
  • Cuando use objetos de valor que se puedan eliminar más adelante, use Map junto con WeakRef


Un ejemplo del último caso de la propuesta:



function makeWeakCached(f) {
  const cache = new Map()
  return key => {
    const ref = cache.get(key)
    if (ref) {
      //     
      const cached = ref.deref()
      if (cached !== undefined) return cached;
    }

    const fresh = f(key)
    //    ( )
    cache.set(key, new WeakRef(fresh))
    return fresh
  };
}

const getImageCached = makeWeakCached(getImage);


  • El constructor WeakRef toma un argumento que debe ser un objeto y devuelve una referencia débil.
  • El método deref de una instancia de WeakRef devuelve uno de dos valores:


En el caso de la caché incorporada, el finalizador está diseñado para completar el proceso de limpieza después de que el recolector de basura destruya el objeto de valor o, más simplemente, para eliminar una referencia débil a dicho objeto.



function makeWeakCached(f) {
  const cache = new Map()
  //    -   
  const cleanup = new FinalizationRegistry(key => {
    const ref = cache.get(key)
    if (ref && !ref.deref()) cache.delete(key)
  })

  return key => {
    const ref = cache.get(key)
    if (ref) {
      const cached = ref.deref()
      if (cached !== undefined) return cached
    }

    const fresh = f(key)
    cache.set(key, new WeakRef(fresh))
    //      ( )
    cleanup.register(fresh, key)
    return fresh
  }
}

const getImageCached = makeWeakCached(getImage);


Lea más sobre finalizadores y cómo usarlos en la propuesta. En general, los finalizadores solo deben usarse cuando sea absolutamente necesario.



Operadores de asignación booleanos



Los operadores de asignación booleanos (una sugerencia de Justin Ridgewell y Hemanth HM ) son una combinación de operadores booleanos (&&, ||, ??) y expresiones de asignación.



A partir de hoy, JavaScript tiene los siguientes operadores de asignación:



=
 

+=
  

-=
  

/=
  

*=
  

&&=
   

||=
   

??=
      (null  undefined -  , 0, false,  '' -  )

**=
    

%=
    

&=
   

|=
   

^=
    

<<=
    

>>=
    

>>>=
       

  
[a, b] = [ 10, 20 ]
{a, b} = { a: 10, b: 20 }


La cláusula le permite combinar operadores lógicos y expresiones de asignación:



a ||= b
// : a || (a = b)
//     ,   "a"  

a &&= b
// : a && (a = b)
//     ,   "a"  

a ??= b
// : a ?? (a = b)
//     ,   "a"   (null  undefined)


Ejemplo de una oración:



//    
function example(opts) {
  //  ,    
  opts.foo = opts.foo ?? 'bar'

  //   ,     
  opts.baz ?? (opts.baz = 'qux')
}

example({ foo: 'foo' })

//    
function example(opts) {
  //     
  opts.foo ??= 'bar'

  //  ""   opts.baz
  opts.baz ??= 'qux';
}

example({ foo: 'foo' })


Separadores de números



Los separadores de números ( sugerencia de Christophe Porteneuve ), o más precisamente, los separadores de números en números, le permiten agregar un carácter de subrayado (_) entre los números para que los números sean más legibles.



Por ejemplo:



const num = 100000000
//     num? 1 ? 100 ? 10 ?


Los separadores resuelven este problema:



const num = 100_000_000 //  : 100 


Los separadores se pueden utilizar tanto en la parte entera como en la decimal de un número:



const num = 1_000_000.123_456


Los separadores se pueden usar no solo en números enteros y de coma flotante, sino también en literales binarios, hexadecimales, octales y BigInt.



Un mayor desarrollo de los separadores de números implica la posibilidad de un uso útil de varios separadores secuenciales y separadores antes y después del número.



¿Quiere probar o mejorar sus conocimientos de JavaScript? Entonces presta atención a mi maravillosa aplicación (no puedes elogiarte a ti mismo ...).



Espero que hayas encontrado algo interesante para ti. Gracias por su atención.



All Articles