It has been 9 years since JavaScript ES6 was introduced.

In ES6 release we’ve got important features as: lexical scoping ie. let and const, classes, arrow functions, template literals, destructuring, maps, sets and default parameters.

I clearly rembember how this release changed writing of JavaScript.

But what about subsequent releases? What have we got since then?

2016 ES7 has brought:

  • Exponentiation operator
    • const square = 4**4
  • Array.prototype.includes()
    • [1,2,3].includes(3) // true

2017 ES8 has brought:

  • async await support
  • Object.entries
    • Object.entries({ a: 1, b: 2 }) // [['a', 1], ['b', 2]]
  • Object.values
    • Object.values({ a: 1, b: 'hello' }) // [1, 'hello']
  • Object.getOwnPropertyDescriptors
  • String padding functions: padStart, padEnd

2018 ES9 has brought:

2019 ES10 has brought:

  • Array.prototype.flat()
    • [1,2,[3,4]].flat() // [1,2,3,4]
  • Array.prototype.flatMap()
    • [1,2].flatMap((x) => [x,x*2]) // [1,2,2,4]
  • Object.fromEntries()
    • Object.fromEntries([['a', 1], ['b', 2]]) // {a:1,b:2}
  • String.prototype new functions trimStart() trimEnd() trimLeft() trimRight()

2020 ES11 has brought:

2021 ES12 has brought:

  • String.prototype.replaceAll()
    • 'aaabbb'.replaceAll('a','c') // cccbbb
  • Promise.any()πŸ“Ž5
  • Logical assignment operators ??=, &&=, ||=
  • WeakRef referencing object but allowing it to be garbage collected
  • _ separator allowed in number literals
    • 1_000_000 // 1000000

2022 ES13 has brought:

  • top-level await
  • public, private, static class fields, static initialization blocksπŸ“Ž6
  • #x in obj syntax, to test for presence of private fields on objects
  • \d new RegExp flagπŸŒŽβ†—
  • TypedArrayπŸŒŽβ†—
  • cause property on Error objects useful when re-throwing error to access original error.πŸŒŽβ†—
  • at() for Strings, Arrays and TypedArrays
    • 'abcd'.at(-1) // d, 'abcd'.at(2) // c
  • Object.hasOwn()πŸŒŽβ†— a better Object.prototype.hasOwnProperty()

2023 ES14 has brought:

  • New methods on Array.prototype
    • toSorted() returns new copy of sorted array
    • toReversed() returns new copy of reversed array
    • with()πŸŒŽβ†—
    • findLast()
    • findLastIndex()
    • toSpliced()
  • #! shebang support

2024 ES15 has brought:

  • \v new RegExp flagπŸŒŽβ†—
  • Promise.withResolvers() convinient promise constructionπŸŒŽβ†—
  • Object.groupBy, Map.groupBy now easier to group items based on property/valueπŸŒŽβ†—
  • Atomics.waitAsync() useful when working with shared memory buffersπŸŒŽβ†—
  • String.prototype.isWellFormed() and String.prototype.toWellFormed() checks for well-formed Unicode

What is your favourite feature from above list?

If you enjoyed this article you can join my newsletter to get dose of weekly craftsmanship.

Get dose of Craftsmanship here


References

πŸ“Ž1 Async iterator support (source)

const asyncIterator = (async function* () {
  yield 1;
  yield 2;
  yield 3;
})();
(async () => {
  for await (const value of asyncIterator) {
    console.log(value);
  }
})();
// Logs: 1, 2, 3

πŸ“Ž2 Async generator support (source)

async function* createAsyncGenerator() {
  yield await Promise.resolve(1);
  yield await Promise.resolve(2);
  yield await Promise.resolve(3);
}
const asyncGen = createAsyncGenerator();
asyncGen.next().then((res) => console.log(res.value)); // 1
asyncGen.next().then((res) => console.log(res.value)); // 2
asyncGen.next().then((res) => console.log(res.value)); // 3

πŸ“Ž3 Promise finally()(source)

Schedules a function to be called when the promise is settled (either fulfilled or rejected).

fetch('https://api.example.com/json-data')
  .then(response => response.json())
  .then(json => console.log(json))
  .catch(error => console.log(error))
  .finally(() => console.log('finally() called!'));

πŸ“Ž4 Promise.allSettled() (source)

This returned promise fulfills when all of the input’s promises settle (including when an empty iterable is passed), with an array of objects that describe the outcome of each promise.

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
  setTimeout(reject, 100, 'foo'),
);
const promises = [promise1, promise2];

Promise.allSettled(promises).then((results) =>
  results.forEach((result) => console.log(result.status)),
);
// Expected output:
// "fulfilled"
// "rejected"

πŸ“Ž5 Promise.any() (source)

const promise1 = Promise.reject(0);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'quick'));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, 'slow'));
const promises = [promise1, promise2, promise3];
Promise.any(promises).then((value) => console.log(value));
// Expected output: "quick"

πŸ“Ž6 public, private, static class fields, static initialization blocks

class ExampleClass {
  instanceField;
  instanceFieldWithInitializer = "instance field";
  static staticField;
  static staticFieldWithInitializer = "static field";
  #privateField;
  #privateFieldWithInitializer = 42;

  static {
    this.staticField = 'Static initialization block'
    // Why? We can use here try...catch block.
    // More than one of these blocks can be declared.
  }

  publicMethod() {}
  static staticMethod() {}
  #privateMethod() {}
  static #staticPrivateMethod() {} // ExampleClass.#staticPrivateMethod()
                                   // allowed to be called only within class
}
// The name of a static property (field or method) cannot be prototype.
// The name of a class field (static or instance) cannot be constructor.