ECMAScript 2025: The New JavaScript Features You Need to Know
Hello HaWkers, another year and another ECMAScript update has been officially approved by TC39. ECMAScript 2025 brings features that might seem incremental at first glance, but they solve real problems that developers face daily.
Have you ever struggled with complex regex or needed to optimize memory in graphics applications? This year's additions address exactly these scenarios.
What Changed in ECMAScript 2025
JavaScript's evolution follows a well-defined annual cycle. Proposals go through stages (Stage 0 to Stage 4) before being incorporated into the official standard. In 2025, we have some interesting additions that are already available in major browsers and runtimes.
Main Additions
Features approved in ES2025:
- Float16Array - New TypedArray for 16-bit precision
- Duplicate Named Capture Groups - More flexible regex
- Set Methods - Native set operations
- Promise.try - Unified error handling
- RegExp.escape - Safe string escaping for regex
- Import Attributes - Metadata in imports
Float16Array: Memory Optimization for Graphics and AI
Float16Array is one of the most significant additions for those working with WebGL, WebGPU or machine learning in the browser.
Why Float16 Matters
Until now, we only had Float32Array and Float64Array. The problem? Many graphics and AI applications don't need that much precision, and using 32 or 64 bits wasted memory.
// Size comparison in bytes for 1000 elements
const float64 = new Float64Array(1000); // 8000 bytes
const float32 = new Float32Array(1000); // 4000 bytes
const float16 = new Float16Array(1000); // 2000 bytes - NEW!
console.log('Float64:', float64.byteLength, 'bytes');
console.log('Float32:', float32.byteLength, 'bytes');
console.log('Float16:', float16.byteLength, 'bytes');
// Float16 uses 50% less memory than Float32!Practical Use with WebGPU
// Creating optimized vertex buffer for WebGPU
const vertices = new Float16Array([
// x, y, z, u, v - coordinates and textures
-1.0, -1.0, 0.0, 0.0, 0.0,
1.0, -1.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 0.5, 1.0,
]);
// Helper functions for conversion
const value = 3.14159;
const f16Bits = Math.f16round(value); // Round to Float16
// Check if value can be represented in Float16
function isFloat16Safe(num) {
const rounded = Math.f16round(num);
return Object.is(num, rounded) ||
(Number.isNaN(num) && Number.isNaN(rounded));
}
console.log(isFloat16Safe(0.1)); // true
console.log(isFloat16Safe(65536)); // false (overflow)DataView with Float16
const buffer = new ArrayBuffer(10);
const view = new DataView(buffer);
// New DataView methods
view.setFloat16(0, 3.14, true); // little-endian
view.setFloat16(2, 2.71, false); // big-endian
const pi = view.getFloat16(0, true);
const e = view.getFloat16(2, false);
console.log(pi); // ~3.140625 (16-bit precision)
console.log(e); // ~2.710938
Duplicate Named Capture Groups
This feature solves a frustrating regex limitation in JavaScript: we couldn't use the same name in alternative capture groups.
The Previous Problem
// BEFORE: This threw a syntax error!
// const dateRegex = /(?<year>\d{4})-(?<month>\d{2})|(?<month>\d{2})\/(?<year>\d{4})/;
// SyntaxError: Duplicate capture group name
// We had to use different names
const dateRegexOld = /(?<year1>\d{4})-(?<month1>\d{2})|(?<month2>\d{2})\/(?<year2>\d{4})/;The ES2025 Solution
// NOW: Works perfectly!
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})|(?<month>\d{2})\/(?<year>\d{4})/;
// Parsing different date formats
const dates = [
'2025-11-30', // ISO format
'30/11/2025', // European format
];
dates.forEach(date => {
const match = date.match(dateRegex);
if (match) {
console.log(`Year: ${match.groups.year}, Month: ${match.groups.month}`);
}
});
// Year: 2025, Month: 11
// Year: 2025, Month: 11Real Use Case: Log Parser
// Flexible parser for different log formats
const logRegex = /
(?:\[(?<level>INFO|WARN|ERROR)\])|
(?:(?<level>info|warn|error):)
/xi;
const logs = [
'[ERROR] Database connection failed',
'warn: Memory usage high',
'[INFO] Server started',
];
logs.forEach(log => {
const match = log.match(logRegex);
if (match) {
const level = match.groups.level.toUpperCase();
console.log(`Level: ${level}`);
}
});
Set Methods: Native Set Operations
We finally have native methods for set operations that we previously had to implement manually.
New Available Methods
const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([4, 5, 6, 7, 8]);
// Union - elements in A or B
const union = setA.union(setB);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]
// Intersection - elements in A and B
const intersection = setA.intersection(setB);
console.log([...intersection]); // [4, 5]
// Difference - elements in A but not in B
const difference = setA.difference(setB);
console.log([...difference]); // [1, 2, 3]
// Symmetric Difference - elements in A or B, but not both
const symmetricDiff = setA.symmetricDifference(setB);
console.log([...symmetricDiff]); // [1, 2, 3, 6, 7, 8]Verification Methods
const admins = new Set(['alice', 'bob']);
const users = new Set(['alice', 'bob', 'charlie', 'diana']);
const guests = new Set(['eve', 'frank']);
// isSubsetOf - is A contained in B?
console.log(admins.isSubsetOf(users)); // true
console.log(users.isSubsetOf(admins)); // false
// isSupersetOf - does A contain B?
console.log(users.isSupersetOf(admins)); // true
// isDisjointFrom - do A and B have no common elements?
console.log(admins.isDisjointFrom(guests)); // true
console.log(admins.isDisjointFrom(users)); // falsePractical Application: Permission System
class PermissionManager {
constructor() {
this.roles = {
admin: new Set(['read', 'write', 'delete', 'manage_users']),
editor: new Set(['read', 'write']),
viewer: new Set(['read']),
};
}
hasAllPermissions(userPermissions, requiredPermissions) {
return requiredPermissions.isSubsetOf(userPermissions);
}
getMissingPermissions(userPermissions, requiredPermissions) {
return requiredPermissions.difference(userPermissions);
}
combineRoles(...roleNames) {
return roleNames.reduce((combined, role) => {
return combined.union(this.roles[role] || new Set());
}, new Set());
}
}
const pm = new PermissionManager();
const userPerms = pm.combineRoles('editor', 'viewer');
const required = new Set(['read', 'write', 'delete']);
console.log('Has all:', pm.hasAllPermissions(userPerms, required)); // false
console.log('Missing:', [...pm.getMissingPermissions(userPerms, required)]); // ['delete']
Promise.try: Unified Error Handling
Promise.try solves a common problem: when you have a function that can be synchronous or asynchronous and want to handle errors uniformly.
The Problem
// BEFORE: Inconsistent code
function processData(data) {
// If this throws a sync error, catch won't get it!
return validateSync(data)
.then(validated => transform(validated))
.catch(err => console.error(err));
}
// Old solution (verbose)
function processDataOld(data) {
return new Promise(resolve => resolve(validateSync(data)))
.then(validated => transform(validated))
.catch(err => console.error(err));
}The Solution with Promise.try
// NOW: Clean and consistent
function processData(data) {
return Promise.try(() => validateSync(data))
.then(validated => transform(validated))
.catch(err => console.error(err));
}
// Works with sync and async functions
const result1 = Promise.try(() => {
return 42; // synchronous
});
const result2 = Promise.try(async () => {
const response = await fetch('/api/data');
return response.json(); // asynchronous
});
// Synchronous errors are caught
const result3 = Promise.try(() => {
throw new Error('Sync error');
}).catch(err => {
console.log('Caught:', err.message); // Caught: Sync error
});RegExp.escape: Safe String Escaping
When you need to use user input in a regex, escaping special characters was manual and error-prone.
// BEFORE: Manual function (common in projects)
function escapeRegExpOld(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// NOW: Native method
const userInput = 'Hello (World)? [Test]';
const escaped = RegExp.escape(userInput);
console.log(escaped); // Hello \(World\)\? \[Test\]
// Safe use in dynamic regex
function highlightText(text, searchTerm) {
const safePattern = RegExp.escape(searchTerm);
const regex = new RegExp(`(${safePattern})`, 'gi');
return text.replace(regex, '<mark>$1</mark>');
}
const result = highlightText(
'Price is $50 (special offer!)',
'$50 (special'
);
// Price is <mark>$50 (special</mark> offer!)
Import Attributes: Metadata in Imports
Import Attributes allow passing additional information when importing modules, especially useful for JSON and other file types.
// Importing JSON with type assertion
import config from './config.json' with { type: 'json' };
// Dynamic import with attributes
const data = await import('./data.json', {
with: { type: 'json' }
});
// This helps engines optimize and validate imports
// Also improves security by making the expected type explicitCompatibility and Adoption
Browser Support
| Feature | Chrome | Firefox | Safari | Node.js |
|---|---|---|---|---|
| Float16Array | 127+ | 129+ | 18.2+ | 22+ |
| Duplicate Named Groups | 125+ | 128+ | 18+ | 22+ |
| Set Methods | 122+ | 127+ | 17+ | 22+ |
| Promise.try | 128+ | In development | 18+ | 22+ |
| RegExp.escape | In development | In development | 18+ | 22+ |
Using Today with Transpilers
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: { node: 'current' },
shippedProposals: true,
}],
],
};Conclusion
ECMAScript 2025 may not have the visual impact of additions like async/await or optional chaining, but each feature solves practical problems. Float16Array will benefit graphics and AI applications, Set Methods simplify code that was previously verbose, and Promise.try standardizes something developers implemented in different ways.
JavaScript's incremental evolution shows the language's maturity. We don't need revolutions every year; well-thought-out point improvements keep the language modern without breaking compatibility.
If you want to follow more news about JavaScript and web development, I recommend checking out the article about Svelte 5 and Runes where we explore another innovation that is transforming how we write reactive code.
Let's go! 🦅
📚 Want to Deepen Your JavaScript Knowledge?
This article covered ECMAScript 2025 features, but there's much more to explore in the JavaScript ecosystem.
Developers who master the fundamentals and keep up with language evolution stand out in the market.
Complete Study Material
If you want to master JavaScript from basics to advanced, I've prepared a complete guide:
Investment options:
- 1x of $4.90 on card
- or $4.90 at sight
👉 Learn About JavaScript Guide
💡 Material updated with industry best practices

