The 5 most transformative JavaScript features from ES14

Last updated on April 06, 2024
The 5 most transformative JavaScript features from ES14

JavaScript has come a long way in the past 10 years with brand new feature upgrades in each one.

Still remember when we created classes like this?

function Car(make, model) {
  this.make = make;
  this.model = model;
}

// And had to join strings like this
Car.prototype.drive = function() {
  console.log("Vroom! This " + this.make +
    " " + this.model + " is driving!");
};

Yeah, a lot has changed!

Let's take a look at the 5 most significant features that arrived in ES14 (2023); and see the ones you missed.

1. toSorted()

Sweet syntactic sugar.

ES14's toSorted() method made it easier to sort an array and return a copy without mutation.

Instead of this:

const nums = [5, 2, 6, 3, 1, 7, 4];

const sorted = clone.sort();

console.log(sorted); // [1, 2, 3, 4, 5, 6, 7]

// ❌❌ Mutated
console.log(nums); // [1, 2, 3, 4, 5, 6, 7]

We now got to do this ✅:

const nums = [5, 2, 6, 3, 1, 7, 4];

// ✅ toSorted() prevents mutation
const sorted = nums.toSorted();

console.log(sorted);  // [1, 2, 3, 4, 5, 6, 7]

console.log(nums);  // [5, 2, 6, 3, 1, 7, 4]

toSorted() takes a callback for controlling sorting behavior - ascending or descending, alphabetical or numeric. Just like sort().

2. Array find from last

Searching from the first item isn't always ideal:

const tasks = [
    { date: '2017-03-05', name: '👟run a 5k' },
    { date: '2017-03-04', name: '🏋️lift 100kg' },
    { date: '2017-03-04', name: '🎶write a song' },
    // 10 million records...
    { date: '2024-04-24', name: '🛏️finally sleep on time' },
    { date: '2024-04-24', name: '📝1h writing with no breaks' },
];

const found = tasks.find((item) => item.date === '2024-03-25');
const foundIndex = tasks.findIndex((item) => item.date === '2024-03-25');

console.log(found); // { value: '2024-03-25', name: 'do 1000 pushups' }
console.log(foundIndex); // 9,874,910

You can easily see that it'll be much faster for me to search our gigantic list from the end instead of start.

const tasks = [
    { date: '2017-03-05', name: 'run a 5k' },
    { date: '2017-03-04', name: 'lift 100kg' },
    { date: '2017-03-04', name: 'write a song' },
    // 10 million records...
    { date: '2024-04-24', name: 'finally sleep on time' },
    { date: '2024-04-24', name: '1h writing with no breaks' },
];

// ✅ Much faster
const found = tasks.findLast((item) => item.date === '2024-03-25');
const foundIndex = tasks.findLastIndex((item) => item.date === '2024-03-25');

console.log(found); // { value: '2024-03-25', name: 'do 1000 pushups' }
console.log(foundIndex); // 9,874,910

And they're also times you MUST search from the end for your program work.

Like we want to find the last even number in a list of numbers, find and findIndex will be incredibly off.

const nums = [7, 14, 3, 8, 10, 9];

// ❌ gives 14, instead of 10
const lastEven = nums.find((value) => value % 2 === 0);

// ❌ gives 1, instead of 4
const lastEvenIndex = nums.findIndex((value) => value % 2 === 0);

console.log(lastEven); // 14
console.log(lastEvenIndex); // 1

And calling reverse() won't work either, even as slow as it would be:

const nums = [7, 14, 3, 8, 10, 9];

// ❌ Copying the entire array with the spread syntax before
// calling reverse()
const reversed = [...nums].reverse();

// correctly gives 10
const lastEven = reversed.find((value) => value % 2 === 0);

// ❌ gives 1, instead of 4
const reversedIndex = reversed.findIndex((value) => value % 2 === 0);

// Need to re-calculate to get original index
const lastEvenIndex = reversed.length - 1 - reversedIndex;

console.log(lastEven); // 10
console.log(reversedIndex); // 1
console.log(lastEvenIndex); // 4

So in cases like where the findLast() and findLastIndex() methods come in handy.

const nums = [7, 14, 3, 8, 10, 9];

// ✅ Search from end
const lastEven = nums.findLast((num) => num % 2 === 0);

// ✅ Maintain proper indexes
const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0);

console.log(lastEven); // 10
console.log(lastEvenIndex); // 4

This code is shorter and more readable. Most importantly, it produces the correct result.

3. toReversed()

Another new Array method to promote immutability and functional programming.

Before - with reverse() ❌:

const arr = [5, 4, 3, 2, 1];

const reversed = arr.reverse();

console.log(reversed); // [1, 2, 3, 4, 5]

// ❌ Original modified
console.log(arr); // [1, 2, 3, 4, 5]

Now - with toReversed() ✅:

const arr = [5, 4, 3, 2, 1];

const reversed = arr.toReversed();

console.log(reversed); // [1, 2, 3, 4, 5]

// ✅ No modification
console.log(arr); // [5, 4, 3, 2, 1]

I find these immutable methods awesome for constantly chaining methods over and over without worrying about the original variables:

// ✅ Results are independent of each other
const nums = [5, 2, 6, 3, 1, 7, 4];

const result = nums
    .toSorted()
    .toReversed()
    .map((n) => n * 2)
    .join();

console.log(result); // 14,12,10,8,6,4,2

const result2 = nums
    .map((n) => 1 / n)
    .toSorted()
    .map((n) => n.toFixed(2))
    .toReversed();

console.log(result2);
// [ '1.00', '0.50', '0.33', '0.25',
//   '0.20', '0.17', '0.14' ]

4. toSpliced()

Lovers of functional programming will no doubt be pleased with all these new Array methods.

This is the immutable counterpart of .splice():

const colors = ['red🔴', 'purple🟣', 'orange🟠', 'yellow🟡'];

// Remove 2 items from index 1 and replace with 2 new items

const spliced = colors.toSpliced(1, 2, 'blue🔵', 'green🟢');

console.log(spliced); // [ 'red🔴', 'blue🔵', 'green🟢', 'yellow🟡' ]

// Original not modified
console.log(colors); // ['red🔴', 'purple🟣', 'orange🟠', 'yellow🟡'];

5. Array with() method

with() is our way of quickly change an array element with no mutation whatsoever.

Instead of this usual way:

const arr = [5, 4, 7, 2, 1]

// Mutates array to change element
arr[2] = 3;

console.log(arr);  // [5, 4, 3, 2, 1]

ES14 now let us do this:

const arr = [5, 4, 7, 2, 1];

const replaced = arr.with(2, 3);

console.log(replaced);  // [5, 4, 3, 2, 1]

// Original not modified
console.log(arr);  // [5, 4, 7, 2, 1]

Final thoughts

They were other features but ES14 was all about easier functional programming and built-in immutability.

With the rise of React we've seen declarative JavaScript explode in popularity; it's only natural that more of them come baked into the language as sweet syntactic sugar.

See also