What's New in JavaScript ES2021

Mar 17, 2021#javascript#news

At the time of writing, following new JavaScript proposal features has made to stage-4 and will almost certainly be included in ES2021. You can already start using in latest versions of browsers, Node.js, and Babel.

  • Numeric Separators (_)
  • Logical Assignment (&&=, ||=, ??=)
  • Weak References (WeakRef and FinalizationRegistry)
  • Promise.any()
  • String.prototype.replaceAll()

Note: ECMAScript is the standard upon which JavaScript is based, managed by TC39 committee. ECMAScript was always an unwanted name that makes everything confusing to beginners. People often talk about JavaScript features but refer to ECMAScript specification.

Numeric Separators

Large numeric literals are difficult for the human eye to parse quickly, especially when there are long digit repetitions. This is a new stage-4 proposal JavaScript language feature enables underscores (_, U+005F) as separators in numeric literals to improve readability.

This feature is designed to have no impact on the interpretation semantics of numeric literals: _ are to be ignored by interpreters and should have no effect. They are meant exclusively as a visual clue to aid development and have no runtime semantics.

It works for numeric literals (integer, bigint, floating-point), fractional, and exponent parts with following limitations:

- No leading or trailing separator.
- No multiple adjacent separator.
- No separator adjacent to leading 0.
- No separator adjacent to BigInt n suffix.
- No separator adjacent to decimal point.
- No separator adjacent to ExponentIndicator.
- No separator adjacent to 0b, 0B, 0o, 0O, 0x, 0X.

Following are valid examples:

// decimal literals
1_000_000_000

// bigint literals
1_000_000_000n

// binary literals
0b1010_0001

// hex literals
0xa0_b0_c0

// octal literals
0o2_2_5_6

// floating-point literals
101_475_938.38

// in fractional part
0.000_001

// in exponent part
1e10_000

Following are invalid examples:

// SyntaxError: not allowed at the end of numeric literals
1_000_000_
0b1010_0001_

// SyntaxError: No multiple adjacent separator
0b1010__0001

// SyntaxError: Can not be used after leading 0
0_11011

More info: TC39 proposal, V8

Logical Assignment

Support logical assignment with the new operators &&=, ||=, and ??=. Unlike their mathematical and bitwise counterparts, logical assignments follow the short-circuiting behavior of their respective logical operations. They only perform an assignment if the logical operation would evaluate the right-hand side.

// falsy: false, 0, -0, 0n, "", null, undefined, and NaN
// truthy: all values are truthy unless defined as falsy
// nullish: null or undefined

a ||= b
// Logical OR assignment
// Equivalent to: a || (a = b);
// Only assigns if a is falsy

a &&= b
// Logical AND assignment
// Equivalent to: a && (a = b);
// Only assigns if a is truthy

a ??= b
// Logical nullish assignment
// Equivalent to: a ?? (a = b);
// Only assigns if a is nullish
  • Assignment (=): Used to assign a value to a variable
  • Logical operators (&&, ||, ??): Typically used with boolean (logical) values, and when they are, they return a boolean value.
  • Short-circuit evaluation: The semantics of some logical operators in which the second argument is executed or evaluated only if the first argument does not suffice to determine the value of the expression.
  • Falsy: JavaScript uses type conversion to coerce any value to a Boolean in contexts that require it, such as conditionals and loops. Following are falsy values: false, 0, 0n, "", null, undefined, NaN.
  • Truthy: A truthy value is a value that is considered true when encountered in a Boolean context, all values are truthy unless they are defined as falsy.
  • Nullish: A nullish value is the value which is either null or undefined, nullish values are always falsy.

More info: TC39 proposal, V8, MDN

Weak References

This feature contains two advanced objects WeakRef and FinalizationRegistry. These interfaces can be used independently or together, depending on the use case. Their correct use takes careful thought, and they are best avoided if possible.

A WeakRef is an object that is used to refer to a target object without preserving it from garbage collection. WeakRefs can dereference to allow access to the target object, if the target object hasn’t been reclaimed by garbage collection.

// Create a WeakRef object referring to a given target object
const ref = new WeakRef(targetObject)

// Return the WeakRef instance's target object, or undefined if the target object has been garbage-collected
const obj = ref.deref()

A FinalizationRegistry object lets you request a callback when an object is garbage-collected.

// Create a registry object that uses the given callback
const registry = new FinalizationRegistry([callback])

// Register an object with a registry instance so that if the object is garbage-collected, the registry's callback may get called
registry.register(target, heldValue, [unregisterToken])

// Unregister a target object from a registry instance
registry.unregister(unregisterToken)

More info: TC39 proposal, V8

Promise.any()

Promise.any() accepts an iterable of promises and returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError holding the rejection reasons if all of the given promises are rejected.

This method is useful for returning the first promise that fulfills. It short-circuits after a promise fulfills, so it does not wait for the other promises to complete once it finds one.

Promise.any(promises).then(
  (first) => {
    // Any of the promises was fulfilled.
  },
  (error) => {
    // All of the promises were rejected.
    // error instanceof AggregateError
  }
)

More info: TC39 proposal, V8, MDN

String.prototype.replaceAll()

Currently there is no way to replace all instances of a substring in a string without use of a global regexp. String.prototype.replace only affects the first occurrence when used with a string argument.

String.prototype.replaceAll() would give developers a straight-forward way to accomplish this common, basic operation.

'aabbcc'.replaceAll('b', '.') // 'aa..cc'
'aabbcc'.replaceAll(/b/g, '.') // 'aa..cc'

More info: TC39 proposal, MDN, V8.