Back to blog

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:

  1. Float16Array - New TypedArray for 16-bit precision
  2. Duplicate Named Capture Groups - More flexible regex
  3. Set Methods - Native set operations
  4. Promise.try - Unified error handling
  5. RegExp.escape - Safe string escaping for regex
  6. 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: 11

Real 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));  // false

Practical 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 explicit

Compatibility 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

Comments (0)

This article has no comments yet 😢. Be the first! 🚀🦅

Add comments