Skip to main content

Top-level `await`

· 5 min read
Myles Borins ([@MylesBorins](https://twitter.com/MylesBorins))

Top-level await enables developers to use the await keyword outside of async functions. It acts like a big async function causing other modules who import them to wait before they start evaluating their body.

Nullish coalescing

· 7 min read
Justin Ridgewell

The nullish coalescing proposal (??) adds a new short-circuiting operator meant to handle default values.

You might already be familiar with the other short-circuiting operators && and ||. Both of these operators handle “truthy” and “falsy” values. Imagine the code sample lhs && rhs. If lhs (read, left-hand side) is falsy, the expression evaluates to lhs. Otherwise, it evaluates to rhs (read, right-hand side). The opposite is true for the code sample lhs || rhs. If lhs is truthy, the expression evaluates to lhs. Otherwise, it evaluates to rhs.

Optional chaining

· 5 min read
Maya Armyanova ([@Zmayski](https://twitter.com/Zmayski)), breaker of optional chains

Long chains of property accesses in JavaScript can be error-prone, as any of them might evaluate to null or undefined (also known as “nullish” values). Checking for property existence on each step easily turns into a deeply-nested structure of if-statements or a long if-condition replicating the property access chain:

Subsume JSON a.k.a. JSON ⊂ ECMAScript

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

With the JSON ⊂ ECMAScript proposal, JSON becomes a syntactic subset of ECMAScript. If you’re surprised that this wasn’t already the case, you’re not alone!

The old ES2018 behavior

In ES2018, ECMAScript string literals couldn’t contain unescaped U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR characters, because they are considered to be line terminators even in that context:

// A string containing a raw U+2028 character.
const LS = '
';
// → ES2018: SyntaxError

// A string containing a raw U+2029 character, produced by `eval`:
const PS = eval('"\u2029"');
// → ES2018: SyntaxError

This is problematic because JSON strings can contain these characters. As a result, developers had to implement specialized post-processing logic when embedding valid JSON into ECMAScript programs to handle these characters. Without such logic, the code would have subtle bugs, or even security issues!

`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);

`Symbol.prototype.description`

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

JavaScript Symbols can be given a description upon creation:

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

Previously, the only way to access this description programmatically was indirectly through Symbol.prototype.toString():

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

However, the code is slightly magical-looking, not very self-explanatory, and violates the “express intent, not implementation” principle. The above technique also doesn’t let you distinguish between a symbol with no description (i.e. Symbol()) and a symbol with the empty string as its description (i.e. Symbol('')).

`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.