Back to Blog
JavaScript9 min readMay 28, 2025

JavaScript 2024: Latest Features and Browser Support

Complete overview of JavaScript's newest features: temporal API, array grouping, pipeline operator, and what's coming in ECMAScript 2024.

JavaScript 2024: Latest Features and Browser Support

JavaScript continues to evolve with exciting new features that make development more efficient and enjoyable. Let's explore the latest additions and upcoming features in ECMAScript 2024.

Temporal API (Stage 3)

The Temporal API provides a modern date and time API that fixes many issues with the legacy Date object.

// Current date and time
const now = Temporal.Now.zonedDateTimeISO()
console.log(now.toString()) // 2024-03-15T10:30:00-05:00[America/New_York]

// Create specific dates
const birthday = Temporal.PlainDate.from('1990-05-15')
const nextBirthday = birthday.with({ year: 2024 })

// Duration calculations
const age = Temporal.Now.plainDateISO().since(birthday)
console.log(`Age: ${age.years} years, ${age.months} months`)

// Working with time zones
const meeting = Temporal.ZonedDateTime.from('2024-03-20T14:00[America/New_York]')
const meetingInTokyo = meeting.withTimeZone('Asia/Tokyo')
console.log(meetingInTokyo.toString()) // Converted to Tokyo time

// Parsing and formatting
const parsed = Temporal.PlainDateTime.from('2024-03-15T10:30:00')
const formatted = parsed.toLocaleString('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: 'numeric',
  minute: 'numeric'
})

Array Grouping

Group array elements by a common key or condition.

const users = [
  { name: 'Alice', role: 'admin', department: 'IT' },
  { name: 'Bob', role: 'user', department: 'Sales' },
  { name: 'Charlie', role: 'admin', department: 'IT' },
  { name: 'Diana', role: 'user', department: 'Marketing' }
]

// Group by role
const byRole = users.group(user => user.role)
console.log(byRole)
// {
//   admin: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
//   user: [{ name: 'Bob', ... }, { name: 'Diana', ... }]
// }

// Group by department
const byDepartment = users.groupToMap(user => user.department)
console.log(byDepartment)
// Map {
//   'IT' => [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
//   'Sales' => [{ name: 'Bob', ... }],
//   'Marketing' => [{ name: 'Diana', ... }]
// }

// Complex grouping
const products = [
  { name: 'Laptop', price: 1200, category: 'Electronics' },
  { name: 'Shirt', price: 30, category: 'Clothing' },
  { name: 'Phone', price: 800, category: 'Electronics' },
  { name: 'Jeans', price: 60, category: 'Clothing' }
]

const expensiveProducts = products.group(product => 
  product.price > 100 ? 'expensive' : 'affordable'
)

Pipeline Operator (Stage 2)

The pipeline operator makes function composition more readable.

// Without pipeline operator
const result = Math.round(Math.max(0, Math.min(100, parseFloat(userInput))))

// With pipeline operator
const result = userInput
  |> parseFloat
  |> _ => Math.min(100, _)
  |> _ => Math.max(0, _)
  |> Math.round

// Data processing pipeline
const processUsers = users => users
  |> _ => _.filter(user => user.active)
  |> _ => _.map(user => ({ ...user, fullName: `${user.firstName} ${user.lastName}` }))
  |> _ => _.sort((a, b) => a.fullName.localeCompare(b.fullName))
  |> _ => _.slice(0, 10)

const topActiveUsers = processUsers(allUsers)

Record and Tuple (Stage 2)

Immutable data structures coming to JavaScript.

// Records (immutable objects)
const point = #{ x: 10, y: 20 }
const newPoint = #{ ...point, x: 15 } // Creates new record

// Tuples (immutable arrays)
const coordinates = #[10, 20, 30]
const newCoordinates = #[...coordinates, 40] // Creates new tuple

// Deep equality
console.log(#{ x: 1, y: 2 } === #{ x: 1, y: 2 }) // true
console.log(#[1, 2, 3] === #[1, 2, 3]) // true

// Nested structures
const user = #{
  id: 1,
  name: 'Alice',
  preferences: #{
    theme: 'dark',
    notifications: #['email', 'push']
  }
}

Pattern Matching (Stage 1)

Powerful pattern matching similar to other functional languages.

// Basic pattern matching
const handleResponse = (response) => match (response) {
  when ({ status: 200, data }) => processData(data),
  when ({ status: 404 }) => showNotFound(),
  when ({ status: 500, error }) => logError(error),
  when ({ status }) if (status >= 400) => handleError(status),
  else => handleUnknown()
}

// Array pattern matching
const processArray = (arr) => match (arr) {
  when ([]) => 'empty',
  when ([x]) => `single: ${x}`,
  when ([x, y]) => `pair: ${x}, ${y}`,
  when ([first, ...rest]) => `first: ${first}, rest: ${rest.length}`,
}

// Object destructuring in patterns
const analyzeUser = (user) => match (user) {
  when ({ role: 'admin', permissions: [...perms] }) if (perms.includes('write')) 
    => 'Full admin access',
  when ({ role: 'admin' }) => 'Read-only admin',
  when ({ role: 'user', verified: true }) => 'Verified user',
  when ({ role: 'user' }) => 'Unverified user',
  else => 'Unknown role'
}

Enhanced Error Handling

New error handling mechanisms for better debugging.

// Error cause chaining
async function fetchUserData(id) {
  try {
    const response = await fetch(`/api/users/${id}`)
    if (!response.ok) {
      throw new Error('Failed to fetch user', { 
        cause: new Error(`HTTP ${response.status}`) 
      })
    }
    return await response.json()
  } catch (error) {
    throw new Error('User data processing failed', { cause: error })
  }
}

// AggregateError for multiple errors
async function processMultipleFiles(files) {
  const errors = []
  const results = []
  
  for (const file of files) {
    try {
      const result = await processFile(file)
      results.push(result)
    } catch (error) {
      errors.push(error)
    }
  }
  
  if (errors.length > 0) {
    throw new AggregateError(errors, 'Some files failed to process')
  }
  
  return results
}

WeakRef and FinalizationRegistry

Better memory management with weak references.

class ResourceManager {
  constructor() {
    this.resources = new Map()
    this.cleanup = new FinalizationRegistry((id) => {
      console.log(`Cleaning up resource ${id}`)
      this.resources.delete(id)
    })
  }
  
  addResource(id, resource) {
    this.resources.set(id, new WeakRef(resource))
    this.cleanup.register(resource, id)
  }
  
  getResource(id) {
    const ref = this.resources.get(id)
    return ref?.deref()
  }
}

// Usage
const manager = new ResourceManager()
let expensiveObject = { data: 'expensive computation result' }
manager.addResource('resource1', expensiveObject)

// Later, when expensiveObject is garbage collected,
// the cleanup callback will run automatically
expensiveObject = null

Top-level await

Use await at the top level of modules.

// config.js
const config = await fetch('/api/config').then(r => r.json())
export default config

// main.js
import config from './config.js'

// Database setup
const db = await connectToDatabase(config.database)

// Start application
const app = createApp(config.app)
app.listen(config.port)

Private Fields and Methods

True privacy in JavaScript classes.

class BankAccount {
  #balance = 0
  #accountNumber
  
  constructor(accountNumber) {
    this.#accountNumber = accountNumber
  }
  
  #validateAmount(amount) {
    if (amount <= 0) {
      throw new Error('Amount must be positive')
    }
  }
  
  deposit(amount) {
    this.#validateAmount(amount)
    this.#balance += amount
    return this.#balance
  }
  
  withdraw(amount) {
    this.#validateAmount(amount)
    if (amount > this.#balance) {
      throw new Error('Insufficient funds')
    }
    this.#balance -= amount
    return this.#balance
  }
  
  getBalance() {
    return this.#balance
  }
}

const account = new BankAccount('123456')
account.deposit(100)
console.log(account.getBalance()) // 100
// console.log(account.#balance) // SyntaxError: Private field '#balance' must be declared in an enclosing class

Browser Support and Polyfills

Stay up-to-date with feature support:

// Feature detection
if ('group' in Array.prototype) {
  // Use native array grouping
  const grouped = items.group(item => item.category)
} else {
  // Use polyfill or custom implementation
  const grouped = groupBy(items, item => item.category)
}

// Polyfill for older browsers
if (!Array.prototype.group) {
  Array.prototype.group = function(callback) {
    const groups = {}
    for (let i = 0; i < this.length; i++) {
      const key = callback(this[i], i, this)
      if (!groups[key]) groups[key] = []
      groups[key].push(this[i])
    }
    return groups
  }
}

These new JavaScript features represent the language's continued evolution toward more expressive, efficient, and maintainable code. Start experimenting with the stable features today!

Enjoyed this article?

Subscribe to our newsletter for more insights on web development, design, and technology.