Ecma International approves ECMAScript 2025: What’s new?

[2025-06-26] dev, javascript, es2025
(Ad, please don’t block)

On 25 June 2025, the 129th Ecma General Assembly approved the ECMAScript 2025 language specification (press release), which means that it’s officially a standard now.

This blog post explains what’s new.

The editors of ECMAScript 2025  

The editors of this release are:

  • Shu-yu Guo
  • Michael Ficarra
  • Kevin Gibbons

What’s new in ECMAScript 2025?  

Import attributes and JSON modules  

Import attributes provide the syntactic foundation for importing non-JavaScript artifacts. The first such artifacts to be supported are JSON modules:

// Static import
import configData1 from './config-data.json' with { type: 'json' };

// Dynamic import
const configData2 = await import(
  './config-data.json', { with: { type: 'json' } }
);

The object literal syntax after with is used for specifying import attributes. type is an import attribute.

Iterator helper methods  

Iterator helper methods let us do more with iterators:

const arr = ['a', '', 'b', '', 'c', '', 'd', '', 'e'];
assert.deepEqual(
  arr.values() // creates an iterator
    .filter(x => x.length > 0)
    .drop(1)
    .take(3)
    .map(x => `=${x}=`)
    .toArray()
  ,
  ['=b=', '=c=', '=d=']
);

Some of the iterator helper methods work like the Array methods with the same names:

  • Methods that return iterators:
    • iterator.filter(filterFn)
    • iterator.map(mapFn)
    • iterator.flatMap(mapFn)
  • Methods that return booleans:
    • iterator.some(fn)
    • iterator.every(fn)
  • Methods that return other values:
    • iterator.find(fn)
    • iterator.reduce(reducer, initialValue?)
  • Methods that return no values:
    • iterator.forEach(fn)

Other iterator helper methods are unique to iterators:

  • iterator.drop(limit)
    • Returns an iterator without the first limit elements of iterator.
  • iterator.take(limit)
    • Returns an iterator with the first limit elements of iterator.
  • iterator.toArray()
    • Collects all remaining elements of iterator in an Array and returns it.

How are iterator methods an improvement over Arrays methods?  

  • Iterator methods can be used with any iterable data structure – e.g., they let us filter and map the data structures Set and Map.
  • Iterator methods don’t create intermediate Arrays and compute data incrementally. That is useful for large amounts of data:
    • With iterator methods, all methods are applied to the first value, then to the second value, etc.
    • With Array methods, the first method is applied to all values, then the second method is applied to all results, etc.

New Set methods  

There are several new Set methods:

  • Combining Sets:
    • Set.prototype.intersection(other)
    • Set.prototype.union(other)
    • Set.prototype.difference(other)
    • Set.prototype.symmetricDifference(other)
  • Checking Set relationships:
    • Set.prototype.isSubsetOf(other)
    • Set.prototype.isSupersetOf(other)
    • Set.prototype.isDisjointFrom(other)

Examples:

assert.deepEqual(
  new Set(['a', 'b', 'c']).union(new Set(['b', 'c', 'd'])),
  new Set(['a', 'b', 'c', 'd'])
);
assert.deepEqual(
  new Set(['a', 'b', 'c']).intersection(new Set(['b', 'c', 'd'])),
  new Set(['b', 'c'])
);
assert.deepEqual(
  new Set(['a', 'b']).isSubsetOf(new Set(['a', 'b', 'c'])),
  true
);
assert.deepEqual(
  new Set(['a', 'b', 'c']).isSupersetOf(new Set(['a', 'b'])),
  true
);

RegExp.escape()  

RegExp.escape() escapes text so that it can be used inside a regular expression – e.g., the following code removes all occurrences of text inside str that are not quoted:

function removeUnquotedText(str, text) {
  const regExp = new RegExp(
    `(?<!“)${RegExp.escape(text)}(?!”)`,
    'gu'
  );
  return str.replaceAll(regExp, '•');
}
assert.equal(
  removeUnquotedText('“yes” and yes and “yes”', 'yes'),
  '“yes” and • and “yes”'
);

Regular expression pattern modifiers (inline flags)  

Regular expression pattern modifiers (inline flags) let us apply flags to parts of a regular expression (vs. all of the regular expression) – for example, in the following regular expression, the flag i is only applied to “HELLO”:

> /^x(?i:HELLO)x$/.test('xHELLOx')
true
> /^x(?i:HELLO)x$/.test('xhellox')
true
> /^x(?i:HELLO)x$/.test('XhelloX')
false

Duplicate named capture groups  

Duplicate named capture groups: We can now use the same group name twice – as long as it appears in different alternatives:

const RE = /(?<chars>a+)|(?<chars>b+)/v;
assert.deepEqual(
  RE.exec('aaa').groups,
  {
    chars: 'aaa',
    __proto__: null,
  }
);
assert.deepEqual(
  RE.exec('bb').groups,
  {
    chars: 'bb',
    __proto__: null,
  }
);

Promise.try()  

Promise.try() lets us start a Promise chain with code that is not purely asynchronous – e.g.:

function computeAsync() {
  return Promise.try(() => {
    const value = syncFuncMightThrow();
    return asyncFunc(value);
  });
}

Support for 16-bit floating point numbers (float16)  

This support provides the following features:

Free book on ECMAScript 2025  

My book “Exploring JavaScript (ES2025 Edition)” is free to read online. Two chapters are especially relevant: