Saltar al contenido principal

39 publicaciones etiquetados con "ECMAScript"

Ver Todas las Etiquetas

Referencias débiles y finalizadores

· 11 min de lectura
Sathya Gunasekaran ([@_gsathya](https://twitter.com/_gsathya)), Mathias Bynens ([@mathias](https://twitter.com/mathias)), Shu-yu Guo ([@_shu](https://twitter.com/_shu)), y Leszek Swirski ([@leszekswirski](https://twitter.com/leszekswirski))

Generalmente, las referencias a los objetos se mantienen fuertemente en JavaScript, lo que significa que mientras tengas una referencia al objeto, no será recolectado por el recolector de basura.

const ref = { x: 42, y: 51 };
// Mientras tengas acceso a `ref` (o cualquier otra referencia al
// mismo objeto), el objeto no será recolectado por el recolector de basura.

Actualmente, WeakMaps y WeakSets son la única forma de referenciar de manera virtualmente débil un objeto en JavaScript: agregar un objeto como una clave a un WeakMap o WeakSet no impide que sea recolectado por el recolector de basura.

const wm = new WeakMap();
{
const ref = {};
const metaData = 'foo';
wm.set(ref, metaData);
wm.get(ref);
// → metaData
}
// Ya no tenemos una referencia a `ref` en este alcance de bloque, así que
// puede ser recolectado por el recolector de basura ahora, incluso aunque sea una clave en `wm` a
// la cual todavía tenemos acceso.

Array.prototype.sort` estable

· 4 min de lectura
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Supongamos que tienes un array de perros, donde cada perro tiene un nombre y una calificación. (Si esto suena a un ejemplo extraño, debes saber que hay una cuenta de Twitter que se especializa exactamente en esto… ¡No preguntes!)

// Nota cómo el array está preordenado alfabéticamente por `name`.
const doggos = [
{ name: 'Abby', rating: 12 },
{ name: 'Bandit', rating: 13 },
{ name: 'Choco', rating: 14 },
{ name: 'Daisy', rating: 12 },
{ name: 'Elmo', rating: 12 },
{ name: 'Falco', rating: 13 },
{ name: 'Ghost', rating: 14 },
];
// Ordena los perros por `rating` en orden descendente.
// (Esto actualiza `doggos` directamente).
doggos.sort((a, b) => b.rating - a.rating);

`Symbol.prototype.description`

· Lectura de un minuto
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Los Symbols de JavaScript pueden tener una descripción al momento de su creación:

const symbol = Symbol('foo');
// ^^^^^

Anteriormente, la única forma de acceder a esta descripción programáticamente era indirectamente a través de Symbol.prototype.toString():

const symbol = Symbol('foo');
// ^^^^^
symbol.toString();
// → 'Symbol(foo)'
// ^^^
symbol.toString().slice(7, -1); // 🤔
// → 'foo'

Sin embargo, el código es un tanto mágico, no muy autoexplicativo, y viola el principio de “expresar intención, no implementación”. La técnica mencionada tampoco permite distinguir entre un símbolo sin descripción (es decir, Symbol()) y un símbolo cuya descripción es la cadena vacía (es decir, Symbol('')).

`Object.fromEntries`

· 4 min de lectura
Mathias Bynens ([@mathias](https://twitter.com/mathias)), susurrador de JavaScript

Object.fromEntries es una adición útil a la biblioteca incorporada de JavaScript. Antes de explicar lo que hace, es útil entender la API Object.entries ya existente.

Object.entries

La API Object.entries ha existido durante un tiempo.

Para cada par clave-valor en un objeto, Object.entries te da un array donde el primer elemento es la clave y el segundo elemento es el valor.

Object.entries es especialmente útil en combinación con for-of, ya que permite iterar elegantemente sobre todos los pares clave-valor en un objeto:

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

for (const [key, value] of entries) {
console.log(`El valor de ${key} es ${value}.`);
}
// Registros:
// El valor de x es 42.
// El valor de y es 50.

Desafortunadamente, no hay una forma fácil de volver del resultado de entries a un objeto equivalente... ¡hasta ahora!

Object.fromEntries

La nueva API Object.fromEntries realiza la inversa de Object.entries. Esto hace que sea fácil reconstruir un objeto basado en sus entradas:

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

const result = Object.fromEntries(entries);
// → { x: 42, y: 50 }

Un caso de uso común es transformar objetos. Ahora puedes hacerlo iterando sobre sus entradas y luego usando métodos de arrays con los que ya puedas estar familiarizado:

const object = { x: 42, y: 50, abc: 9001 };
const result = Object.fromEntries(
Object.entries(object)
.filter(([ key, value ]) => key.length === 1)
.map(([ key, value ]) => [ key, value * 2 ])
);
// → { x: 84, y: 100 }

En este ejemplo, usamos filter para obtener solo las claves de longitud 1, es decir, solo las claves x y y, pero no la clave abc. Luego usamos map sobre las entradas restantes y devolvemos un par clave-valor actualizado por cada una. En este ejemplo, duplicamos cada valor multiplicándolo por 2. El resultado final es un nuevo objeto con solo las propiedades x y y, y los nuevos valores.

Combinadores de Promesas

· 5 min de lectura
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Desde la introducción de las promesas en ES2015, JavaScript ha soportado exactamente dos combinadores de promesas: los métodos estáticos Promise.all y Promise.race.

Actualmente, dos nuevas propuestas están atravesando el proceso de estandarización: Promise.allSettled, y Promise.any. Con estas adiciones, habrá un total de cuatro combinadores de promesas en JavaScript, cada uno permitiendo diferentes casos de uso.

`Array.prototype.flat` y `Array.prototype.flatMap`

· 2 min de lectura
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Array.prototype.flat

El arreglo en este ejemplo tiene varios niveles de profundidad: contiene un arreglo que a su vez contiene otro arreglo.

const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ arreglo exterior
// ^^^^^^^^ arreglo interior
// ^^^ arreglo más interior

Array#flat devuelve una versión aplanda de un arreglo dado.

array.flat();
// → [1, 2, [3]]

// …es equivalente a:
array.flat(1);
// → [1, 2, [3]]

La profundidad predeterminada es 1, pero puedes pasar cualquier número para aplanar recursivamente hasta esa profundidad. Para seguir aplanando recursivamente hasta que el resultado no contenga más arreglos anidados, pasamos Infinity.

// Aplanar recursivamente hasta que el arreglo no contenga más arreglos anidados:
array.flat(Infinity);
// → [1, 2, 3]

¿Por qué este método se llama Array.prototype.flat y no Array.prototype.flatten? ¡Lee nuestro artículo #SmooshGate para descubrirlo!

Array.prototype.flatMap

Aquí hay otro ejemplo. Tenemos una función duplicate que toma un valor y devuelve un arreglo que contiene ese valor dos veces. Si aplicamos duplicate a cada valor en un arreglo, terminamos con un arreglo anidado.

const duplicate = (x) => [x, x];

[2, 3, 4].map(duplicate);
// → [[2, 2], [3, 3], [4, 4]]

Entonces puedes llamar a flat sobre el resultado para aplanar el arreglo:

[2, 3, 4].map(duplicate).flat(); // 🐌
// → [2, 2, 3, 3, 4, 4]

Dado que este patrón es tan común en la programación funcional, ahora hay un método dedicado llamado flatMap para ello.

[2, 3, 4].flatMap(duplicate); // 🚀
// → [2, 2, 3, 3, 4, 4]

flatMap es un poco más eficiente en comparación con realizar un map seguido de un flat por separado.

¿Interesado en casos de uso de flatMap? Consulta la explicación de Axel Rauschmayer.

Compatibilidad con Array#{flat,flatMap}

Separadores numéricos

· 2 min de lectura
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Los literales numéricos grandes son difíciles de interpretar rápidamente para el ojo humano, especialmente cuando hay muchos dígitos repetidos:

1000000000000
1019436871.42

Para mejorar la legibilidad, una nueva característica del lenguaje JavaScript permite guiones bajos como separadores en literales numéricos. Por lo tanto, lo anterior ahora puede reescribirse para agrupar los dígitos por miles, por ejemplo:

`String.prototype.matchAll`

· 3 min de lectura
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Es común aplicar repetidamente la misma expresión regular en una cadena para obtener todas las coincidencias. Hasta cierto punto, esto ya es posible hoy en día utilizando el método String#match.

En este ejemplo, encontramos todas las palabras que consisten únicamente en dígitos hexadecimales y luego registramos cada coincidencia:

const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// Salida:
//
// 'DEADBEEF'
// 'CAFE'

Sin embargo, esto solo te da las subcadenas que coinciden. Normalmente, no solo quieres las subcadenas, también deseas información adicional como el índice de cada subcadena o los grupos de captura dentro de cada coincidencia.

Ya es posible lograr esto escribiendo tu propio bucle y haciendo un seguimiento de los objetos de coincidencia tú mismo, pero es un poco tedioso y no muy conveniente:

const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// Salida:
//
// [ 'DEADBEEF', índice: 19, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
// [ 'CAFE', índice: 28, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]

La nueva API String#matchAll lo hace más fácil que nunca: ahora puedes escribir un simple bucle for-of para obtener todos los objetos de coincidencia.

const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// Salida:
//
// [ 'DEADBEEF', índice: 19, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
// [ 'CAFE', índice: 28, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]

String#matchAll es especialmente útil para expresiones regulares con grupos de captura. Te proporciona toda la información de cada coincidencia individual, incluidos los grupos de captura.

const string = 'Repositorios favoritos de GitHub: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} en ${match.index} con '${match.input}'`);
console.log(`→ owner: ${match.groups.owner}`);
console.log(`→ repo: ${match.groups.repo}`);
}

Exportación de nombres de espacios en módulos

· Lectura de un minuto
Mathias Bynens ([@mathias](https://twitter.com/mathias))

En módulos de JavaScript, ya era posible usar la siguiente sintaxis:

import * as utils from './utils.mjs';

Sin embargo, no existía una sintaxis de export simétrica… hasta ahora:

export * as utils from './utils.mjs';

Esto es equivalente a lo siguiente:

import * as utils from './utils.mjs';
export { utils };

Campos de clase públicos y privados

· 5 min de lectura
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Varias propuestas amplían la sintaxis existente de clases en JavaScript con nuevas funcionalidades. Este artículo explica la nueva sintaxis de campos de clase públicos en V8 v7.2 y Chrome 72, así como la próxima sintaxis de campos de clase privados.

Aquí hay un ejemplo de código que crea una instancia de una clase llamada IncreasingCounter:

const counter = new IncreasingCounter();
counter.value;
// registros '¡Obteniendo el valor actual!'
// → 0
counter.increment();
counter.value;
// registros '¡Obteniendo el valor actual!'
// → 1

Nota que acceder a value ejecuta algún código (es decir, registra un mensaje) antes de devolver el resultado. Ahora pregúntate, ¿cómo implementarías esta clase en JavaScript? 🤔

Sintaxis de clases de ES2015

Aquí se muestra cómo IncreasingCounter podría implementarse utilizando la sintaxis de clases de ES2015:

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('¡Obteniendo el valor actual!');
return this._count;
}
increment() {
this._count++;
}
}

La clase instala el getter value y un método increment en el prototipo. Más interesante aún, la clase tiene un constructor que crea una propiedad de instancia _count y establece su valor predeterminado en 0. Actualmente, tendemos a usar el prefijo de guion bajo para denotar que _count no debería ser utilizado directamente por los consumidores de la clase, pero eso es solo una convención; no es realmente una propiedad "privada" con semántica especial aplicada por el lenguaje.