MacMusic  |  PcMusic  |  440 Software  |  440 Forums  |  440TV  |  Zicos
set
Search

ECMAScript 2025: The best new features in JavaScript

Wednesday July 16, 2025. 11:00 AM , from InfoWorld
This year’s update to the JavaScript specification covers a lot of ground. The headline addition is the new built-in Iterator object and its functional operators. Other updates include new Set methods, a direct JSON module import, improvements to regular expressions, a new Promise.try method for streamlining Promise chains, and a new Float16Array typed array.

Let’s take a tour of JavaScript’s newest features and what you can do with them.

The Iterator object

We’ll start with the most far-reaching addition, which the spec describes as “a new Iterator global with associated static and prototype methods for working with iterators.”

Everything here begins with the built-in global Iterator. (If you hit F12 and open the JavaScript console in Devtools, you can see the object is there.) This object lets you wrap existing iterable objects in a new interface that provides functional operators like map and filter.

The most exciting part of this wrapper is twofold: It gives a syntax improvement by providing functional operators on iterables that don’t have them, and its implementation of these is lazy, with in-line evaluation of elements. This yields performance benefits, especially for large or streaming collections.

The new Iterator also lets you wrap simple iterators that don’t have functional operators, like function generators. That means Array and other iterables can be handled within the same consistent API, benefiting from better performance.

I should emphasize that JavaScript’s Arrays, while they do have built-in functional operators, work by eagerly evaluating the entire array and producing intermediate “working arrays” at each stage of the operation. So, any time you call map or filter, you are implying the background creation of a subarray. Iterator works like other functional programming-style APIs (Java Streams, for example), where each operator is processed element-wise and a new collection is only created when a terminal operator is reached.

A couple of short examples will demonstrate how this works. First, say we have an array of smart people:

let smarties = ['Plato','Lao Tzu','St. Augustine','Ibn Arabi','Max Planck','David Bohm'];

If we wanted to convert this array to use Iterator’s improved map function, we could do this:

Iterator.from(smarties).map(x => x.length).toArray();  // [5, 7, 13, 9, 10, 10]

We use the from() to create the Iterator object, while toArray is the terminal operator. As a reminder, you could also do this with Array.map, but the implementation of Iterator.map() is lazy under the hood. To see this in action, let’s say we were filtering on length:

Iterator.from(smarties).map(x => x.length).filter(x => x < 10).toArray(); // [5, 7, 9]

Adding the take() operator “short circuits” this process by indicating we are only interested in the first two elements that meet our criteria. This means only the part of the source array needed to fulfill the criteria will be processed:

Iterator.from(smarties).map(x => x.length).filter(x => x < 10).take(2).toArray() // [5, 7]

Next, we want to see how Iterator wraps a non-array iterator. We’ll start with a simple generator that produces the same output:

function* getSmartiesNames() {
yield 'Plato';
yield 'Lao Tzu';
yield 'St. Augustine';
yield 'Ibn Arabi';
yield 'Max Planck';
yield 'David Bohm';
}

Now we wrap that in an Iterator:

terator.from(getSmartiesNames()).map(name => name.length).filter(length => length < 10).take(2).toArray();

Notice the Array and generator now have the same functional API, and both benefit from the underlying optimization. (Of course, these examples are intentionally very simple.)

New Set methods

The Set class isn’t as often used in JavaScript as other languages because it was a later addition, and because the Array object is so ubiquitous and flexible. However, Set offers a guaranteed unique, unsorted collection that preserves insertion order and linear time for the has and delete operations. The addition of new methods derived from set theory makes it even more useful.

These methods are easy to understand, especially if you’ve already used them in a language like SQL. They’re also very handy.

Set.intersection finds the elements that are the same in two sets:

let set =new Set(['A','B','C']);
let set2 = new Set(['C','D','E']);

set.intersection(set2); // yields {‘C’}

Notice that here the ‘C’ element is only present once in the resulting Set because sets do not hold duplicates.

Set.difference “subtracts” the right set from the left:

set.difference(set2); // yields {“A”,”B”}

This leaves us with whatever was in the first set but not in the second:

set.symetricDifference(set2); // yields {'A', 'B', 'D', 'E'}

Notice the ‘C’ element was dropped in this sample, because it was the only one shared by both sets.

The spec also includes three new methods for checking set relationships. These are fairly self-explanatory:

isSubsetOf checks whether the first set is entirely contained by the second.

isSupersetOf checks whether the first set entirely contains the second.

isDisjointFrom checks whether the two sets are entirely different.

Import JSON as a module

ES2025 standardizes the ability to import JSON directly as a module. This avoids the need to manually import JSON files or use a build step for that purpose. It makes importing JSON about as simple as you could hope:

import appConfig from './data.json' with { type: 'json' };

The contents of appConfig are now the actual JSON-parsed object of the file (not a string). This only works for local files, not remote locations.

The with keyword points to the module attributes the spec is referring to. Right now, this keyword is used only to specify a JSON module’s type as JSON (it was added to avoid the possibility of interpreting the contents as JavaScript). In the future, with could be used to designate other module attributes.

Improvements to regular expressions

The new RegExp.escape static method lets you prevent injection attacks on regular expression strings. It will escape any characters with special meaning in a regex context, analogous to similar functions that prevent SQL injection on arbitrary SQL strings. This lets you safely consume untrusted regex strings.

Imagine if you have a part of your system that takes user input and combines it with regular expressions. A malicious (or puckish) user could add regex expressions that would throw syntax errors. Just run RexExp.escape(userInput) first, though, and that problem is prevented.

Another improvement is noted in the spec as: “added syntax for enabling and disabling modifier flags inline within regular expressions.”

If you are an aficionado of regular expressions, this is a bread-and-butter kind of improvement. For the rest of us, it might be a tad esoteric. The essence is to allow you to apply flags to only small internal sections of an expression. In the past, flags (like case sensitivity) applied to the whole expression. There was no way to make a flag apply to only a subsection of the expression without either a convoluted workaround or breaking it into multiple regular expressions and executions.

Now you can define subsections that apply their own flags. Let’s imagine we have these snippets from some famous Beatles’ songs:

const beatlesLyrics = [
'All You Need Is Love',
'Can't Buy Me Love',
'A Hard Day's Night',
'Eight Days a Week',
'Yesterday, all my troubles seemed so far away.',
'Love Me Do'
];

What we want is to match “love” case-insensitive, but “Day” case-sensitive.  There was no good way to do that before, but now it’s pretty easy:

const beatlesSearchRegex = /(?i:Love)|(?-i:Day)/;

This tells us to match the two parenthetical parts (the question mark indicates it is a non-capturing group; one that does not maintain a reference) with independant flags applied. In this case, we apply case insensitivity (i) to Love, but not to Day. Because the day is lowercase, we do not match on the “Yesterday” in this set of strings.

The new Promise.try method

According to the spec, the new Promise.try method is for “calling functions which may or may not return a Promise and ensuring the result is always a Promise.”

This is a slick addition. It lets you wrap a promise chain in Promise.try() so that any synchronous errors that bubble out will be handled by the chained.catch(). That means you can skip the extra try{} block and use your catch() to handle both asynchronous and synchronous errors:

Promise.try(() => thisMightThrowSyncError('foobar')).then(val => console.log('All is well', val)).catch(err => console.error('Error:', err.message));

Promise.try will also handle all errors, synchronous or asynchronous, for the operations that are chained within, so you can chain promises without breaking them up for try blocks:

Promise.try(() => possibleSyncError('foo')).then(processedStep1 => possibleAsyncError('bar')).then(processedStep2 => fanotherPossibleAssyncError('baz')).then(finalResult => console.log('Success', finalResult)).catch(error => console.error('All errors arrive here',
error.message, 'I am an error'));

Another power of Promise.try is that it intelligently handles operations that return simple values or a Promise. Imagine you have a function that may return a cache value if it hits, or a Promise if it makes a network call:

function fetchData(key) {
if (cache[key]) {
return cache[key];
} else {
return new Promise(resolve => {
setTimeout(() => { data = { id: key, name: `New Item ${key}`, source: 'DB' };
cache[key] = data;
}, 500);
});
}
}

Whether that hits or not, Promise.try will accept the call and route all errors to the catch block:

Promise.try(() => fetchData('user:1')).then(data => console.log('Result 1 (from cache):', data)).catch(error => console.error('Error 1:', error.message));

Float16Array TypedArray

This update is described in the spec as “added a new Float16Array TypedArray kind as well as the related DataView.prototype.getFloat16, DataView.prototype.setFloat16, and Math.f16round methods.”

The Float16 type is used in high-performance computing, where the application needs to maximize the memory usage by trading footprint for precision. This includes machine learning, which is probably the driver for this addition.

JavaScript has not added a new scalar numeric type. It still only has Number, but it now has Float16Array for holding collections of this type. JavaScript Number uses double precision 64-bit floating point format; however, in cases where you are using large amounts of numbers, where less precision is acceptable (as in neural network weights and biases), using 16-bit can be a big optimization.

The added methods are all for taking raw binary data buffers and working with Float16Array:

DataView.prototype.getFloat16 allows you to read a Float16 off the dataview (it will be returned as its 64 bit representation):

newView.getFloat16(0, true);

DataView.prototype.setFloat16 allows you to write a Float16 to the dataview:

view.setFloat16(0, 3.14159, true);

Math.f16round checks if a Number will suffer a loss of precision when converted to Float16.

Conclusion

JavaScript’s stewards have kept up the effort to deliver useful and relevant new language features to the specification. This set of new standardized features feels well-considered and balanced, and it invites a range of uses showcased here.
https://www.infoworld.com/article/4021944/ecmascript-2025-the-best-new-features-in-javascript.html

Related News

News copyright owned by their original publishers | Copyright © 2004 - 2025 Zicos / 440Network
Current Date
Jul, Thu 17 - 16:34 CEST