Skip to main content

13 posts tagged with "io19"

View All Tags

`Intl.NumberFormat`

· 4 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias)) and Shane F. Carr

You might already be familiar with the Intl.NumberFormat API, as it’s been supported across modern environments for a while now.

In its most basic form, Intl.NumberFormat lets you create a reusable formatter instance that supports locale-aware number formatting. Just like other Intl.*Format APIs, a formatter instance supports both a format and a formatToParts method:

`globalThis`

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

If you’ve written JavaScript for use in a web browser before, you may have used window to access the global this. In Node.js, you may have used global. If you’ve written code that must work in either environment, you may have detected which of these is available, and then used that — but the list of identifiers to check grows with the number of environments and use cases you want to support. It gets out of hand quickly:

Weak references and finalizers

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

Generally, references to objects are strongly held in JavaScript, meaning that as long you have a reference to the object, it won’t be garbage-collected.

const ref = { x: 42, y: 51 };
// As long as you have access to `ref` (or any other reference to the
// same object), the object won’t be garbage-collected.

Currently, WeakMaps and WeakSets are the only way to kind-of-weakly reference an object in JavaScript: adding an object as a key to a WeakMap or WeakSet doesn’t prevent it from being garbage-collected.

const wm = new WeakMap();
{
const ref = {};
const metaData = 'foo';
wm.set(ref, metaData);
wm.get(ref);
// → metaData
}
// We no longer have a reference to `ref` in this block scope, so it
// can be garbage-collected now, even though it’s a key in `wm` to
// which we still have access.

Stable `Array.prototype.sort`

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

Let’s say you have an array of dogs, where each dog has a name and a rating. (If this sounds like a weird example, you should know that there’s a Twitter account that specializes in exactly this… Don’t ask!)

// Note how the array is pre-sorted alphabetically by `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 },
];
// Sort the dogs by `rating` in descending order.
// (This updates `doggos` in place.)
doggos.sort((a, b) => b.rating - a.rating);

`Object.fromEntries`

· 4 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias)), JavaScript whisperer

Object.fromEntries is a useful addition to the built-in JavaScript library. Before explaining what it does, it helps to understand the pre-existing Object.entries API.

Object.entries

The Object.entries API has been around for a while.

For each key-value pair in an object, Object.entries gives you an array where the first element is the key, and the second element is the value.

Object.entries is especially useful in combination with for-of, as it enables you to very elegantly iterate over all key-value pairs in an object:

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

for (const [key, value] of entries) {
console.log(`The value of ${key} is ${value}.`);
}
// Logs:
// The value of x is 42.
// The value of y is 50.

Unfortunately, there’s no easy way to go from the entries result back to an equivalent object… until now!

Object.fromEntries

The new Object.fromEntries API performs the inverse of Object.entries. This makes it easy to reconstruct an object based on its entries:

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

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

One common use case is transforming objects. You can now do this by iterating over its entries, and then using array methods you might already be familiar with:

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 }

In this example, we’re filtering the object to only get keys of length 1, that is, only the keys x and y, but not the key abc. We then map over the remaining entries and return an updated key-value pair for each. In this example, we double each value by multiplying it by 2. The end result is a new object, with only properties x and y, and the new values.

Promise combinators

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

Since the introduction of promises in ES2015, JavaScript has supported exactly two promise combinators: the static methods Promise.all and Promise.race.

Two new proposals are currently making their way through the standardization process: Promise.allSettled, and Promise.any. With those additions, there’ll be a total of four promise combinators in JavaScript, each enabling different use cases.

`Array.prototype.flat` and `Array.prototype.flatMap`

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

Array.prototype.flat

The array in this example is several levels deep: it contains an array which in turn contains another array.

const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ outer array
// ^^^^^^^^ inner array
// ^^^ innermost array

Array#flat returns a flattened version of a given array.

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

// …is equivalent to:
array.flat(1);
// → [1, 2, [3]]

Numeric separators

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

Large numeric literals are difficult for the human eye to parse quickly, especially when there are lots of repeating digits:

1000000000000
1019436871.42

To improve readability, a new JavaScript language feature enables underscores as separators in numeric literals. So, the above can now be rewritten to group the digits per thousand, for example:

`String.prototype.matchAll`

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

It’s common to repeatedly apply the same regular expression on a string to get all the matches. To some extent, this is already possible today by using the String#match method.

In this example, we find all words that consist of hexadecimal digits only, and then log each match:

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// Output:
//
// 'DEADBEEF'
// 'CAFE'

However, this only gives you the substrings that match. Usually, you don’t just want the substrings, you also want additional information such as the index of each substring, or the capturing groups within each match.

It’s already possible to achieve this by writing your own loop, and keeping track of the match objects yourself, but it’s a little annoying and not very convenient:

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// Output:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

The new String#matchAll API makes this easier than ever before: you can now write a simple for-of loop to get all the match objects.

const string = 'Magic hex numbers: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// Output:
//
// [ 'DEADBEEF', index: 19, input: 'Magic hex numbers: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magic hex numbers: DEADBEEF CAFE' ]

String#matchAll is especially useful for regular expressions with capture groups. It gives you the full information for each individual match, including capturing groups.

const string = 'Favorite GitHub repos: 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]} at ${match.index} with '${match.input}'`);
console.log(`→ owner: ${match.groups.owner}`);
console.log(`→ repo: ${match.groups.repo}`);
}

`Intl.ListFormat`

· 3 min read
Mathias Bynens ([@mathias](https://twitter.com/mathias)) and Frank Yung-Fong Tang

Modern web applications often use lists consisting of dynamic data. For example, a photo viewer app might display something like:

This photo includes Ada, Edith, and Grace.

A text-based game might have a different kind of list:

Choose your superpower: invisibility, psychokinesis, or empathy.

Since each language has different list formatting conventions and words, implementing a localized list formatter is non-trivial. Not only does this require a list of all the words (such as “and” or “or” in the above examples) for each language you want to support — in addition you need to encode the exact formatting conventions for all those languages! The Unicode CLDR provides this data, but to use it in JavaScript, it has to be embedded and shipped alongside the other library code. This unfortunately increases the bundle size for such libraries, which negatively impacts load times, parse/compile cost, and memory consumption.