DEV Community

Tanvir Azad
Tanvir Azad

Posted on

JS Evolution: 15 Years in 15 Minutes

JavaScript has come a long way since its humble beginnings. From the basic scripting language of 1995 to the powerful, feature-rich language we use today, JavaScript continues to evolve rapidly. This comprehensive comparison will explore the journey from ES5 (2009) through ES6/ES2015 to the latest ES2024 features.


Feature ES2024 ES6 / ES2015 ES5 (2009)
Variable Declaration 'let' & 'const' 'let' & 'const' 'var' only
Arrow Functions (a, b) => a + b (a, b) => a + b
Object Destructuring const { age } = user const { age } = user var user = {age: 25}; var age = user.age
Spread Operator const obj2 = { ...obj1 } const obj2 = { ...obj1 }
String Interpolation `Hello ${userName}!` `Hello ${userName}!` "Hello" + userName + "!";
Modules (Export/Import) import obj from '...' import obj from '...' var obj = require('...')
Classes class Person {...} class Person {...}
Promises new Promise(...) new Promise(...)
Top-Level await const data = await fetch(...);
Private Class Fields class A { #x = 42 }
BigInt 123456789012345678901234567890n;
Array .at() Method const last = arr.at(-1);
Object.hasOwn() Object.hasOwn(obj, 'prop');
Hashbang Grammar #!/usr/bin/env node
Set Union a.union(b);
Set Intersection a.intersection(b);
Set Difference a.difference(b);
Set Disjoint Check a.isDisjointFrom(b);
Set Symmetric Difference a.symmetricDifference(b);
Set Subset/Superset a.isSubsetOf(b); a.isSupersetOf(b);

ES2024 Features - In-Depth Examples

Let's dive into the newest JavaScript features that ES2024 brings to the table, complete with practical examples you can use in your projects today.


1. Top-Level await

One of the most requested features finally arrived with ES2022 and continues to be refined in ES2024. You can now use await at the top level of modules without wrapping it in an async function.

// Before (ES5/ES6) - Required wrapping in async function
(async () => {
  const response = await fetch('/api/config');
  const config = await response.json();
})();

// ES2024 - Clean and simple
const response = await fetch('/api/config');
const config = await response.json();
Enter fullscreen mode Exit fullscreen mode

2. Private Class Fields

Encapsulation is finally first-class in JavaScript with private fields using the # syntax.

class Account {
  #balance = 0; // private field

  constructor(amount) {
    this.#balance = amount;
  }

  deposit(amount) {
    this.#balance += amount;
  }

  #format() {
    return `$${this.#balance}`;
  }

  getBalance() {
    return this.#format();
  }
}

const acc = new Account(100);
acc.deposit(50);
console.log(acc.getBalance()); // $150

console.log(acc.#balance); 
// ❌ SyntaxError: Private field '#balance' must be declared in an enclosing class
Enter fullscreen mode Exit fullscreen mode

3. BigInt for Large Numbers

JavaScript's number limitations are addressed with BigInt, allowing you to work with arbitrarily large integers.

// Traditional number limitations
const maxSafeInteger = Number.MAX_SAFE_INTEGER; // 9007199254740991
const problematic = maxSafeInteger + 1; // 9007199254740992
const moreProblem = maxSafeInteger + 2; // 9007199254740992 (same as above!)

// BigInt solution
const bigNumber = 123456789012345678901234567890n;
const anotherBig = BigInt('987654321098765432109876543210');

// Arithmetic operations
const sum = bigNumber + anotherBig;
const product = bigNumber * 2n;
Enter fullscreen mode Exit fullscreen mode

4. Array .at() Method

Access array elements with support for negative indices, making it easier to get elements from the end.

const fruits = ['apple', 'banana', 'orange', 'grape'];

// Traditional approach
const lastFruit = fruits[fruits.length - 1]; // 'grape'
const secondLast = fruits[fruits.length - 2]; // 'orange'

// ES2024 .at() method
const lastFruit2 = fruits.at(-1); // 'grape'
const secondLast2 = fruits.at(-2); // 'orange'
const firstFruit = fruits.at(0); // 'apple'

// Particularly useful in function chains
const processedItems = getData()
  .filter(item => item.active)
  .map(item => item.name)
  .at(-1); // Get the last processed item
Enter fullscreen mode Exit fullscreen mode

5. Object.hasOwn()

A safer alternative to Object.prototype.hasOwnProperty.call() for checking object properties.

const user = {
  name: 'John',
  age: 30,
  email: 'john@example.com'
};

// Old approach (potentially unsafe)
if (user.hasOwnProperty('name')) {
  console.log('Has name property');
}

// Safer but verbose approach
if (Object.prototype.hasOwnProperty.call(user, 'name')) {
  console.log('Has name property');
}

// ES2024 clean approach
if (Object.hasOwn(user, 'name')) {
  console.log('Has name property');
}
Enter fullscreen mode Exit fullscreen mode

6. Hashbang Grammar

Support for shebang (#!) syntax in JavaScript files, making them executable on Unix-like systems.

#!/usr/bin/env node

// This is now valid JavaScript syntax
console.log('Hello from a directly executable JS file!');

// Make the file executable and run it directly:
// chmod +x script.js
// ./script.js
Enter fullscreen mode Exit fullscreen mode

7. Set Operations

ES2024 introduces powerful set operations that make working with collections much more intuitive.

const developers = new Set(['Alice', 'Bob', 'Charlie']);
const designers = new Set(['Bob', 'David', 'Eve']);
const managers = new Set(['Alice', 'Frank']);

// Union - combine all unique values
const allEmployees = developers.union(designers).union(managers);
console.log(allEmployees); // Set(6) {'Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'}

// Intersection - find common elements
const versatileEmployees = developers.intersection(designers);
console.log(versatileEmployees); // Set(1) {'Bob'}

// Difference - elements in first set but not in second
const onlyDevelopers = developers.difference(designers);
console.log(onlyDevelopers); // Set(2) {'Alice', 'Charlie'}

// Symmetric difference - elements in either set but not both
const specializedEmployees = developers.symmetricDifference(designers);
console.log(specializedEmployees); // Set(4) {'Alice', 'Charlie', 'David', 'Eve'}

// Check relationships between sets
console.log(developers.isDisjointFrom(new Set(['Xavier', 'Yuki']))); // true
console.log(new Set(['Alice']).isSubsetOf(developers)); // true
console.log(developers.isSupersetOf(new Set(['Alice', 'Bob']))); // true
Enter fullscreen mode Exit fullscreen mode

Conclusion

ES2024 brings powerful new features that make JavaScript more expressive, safer, and more convenient to use. From better error handling with cause chains to efficient set operations, these features help write cleaner, more maintainable code.

The evolution from ES5 to ES2024 shows JavaScript's commitment to addressing real-world developer needs while maintaining backward compatibility. Whether you're building modern web applications or Node.js services, these features will help you write better code.

Start experimenting with these features in your projects today, and watch how they improve your development experience!


Have you tried any of these ES2024 features in your projects? Share your experiences in the comments below!

Top comments (0)