The 5 most transformative JavaScript features from ES13

ES13 was packed with valuable features that completely transformed the way we write JavaScript.

Code became cleaner, shorter, and easier to write.

Let’s check them out and see the ones you missed.

1. Top-level await

With ES13 we were finally able to use await in the global scope!

❌ Before ES13:

JavaScript
// ❌ Syntax error: await is only valid in async functions await setTimeoutAsync(3000); function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve('codingbeautydev.com'); }, timeout); }); }

We always had to put it in an async function or create an async IIFE:

JavaScript
// async IIFE (async () => { await setTimeoutAsync(3000); })(); // à la C++ async function main() { await setTimeoutAsync(3000); }

✅ After ES13:

JavaScript
// ✅ Waits for timeout - no error thrown await setTimeoutAsync(3000); function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve('codingbeautydev.com'); }, timeout); }); }

2. Class declaration upgrades

Class field declarations

Before ES13 we could only declare class fields in the constructor!

Unlike in many other languages we could not declare or define them in the outermost scope of the class.

❌ Before:

JavaScript
class Site { constructor() { // ❌ wouldn't work outside this.url = 'codingbeautydev.com'; this.color = '🔵blue'; } } const site = new Site(); console.log(site.url); // codingbeautydev.com console.log(color); // blue

✅ Now with ES13:

Just like in TypeScript:

JavaScript
class Site { // ✅ no constructor needed url = 'codingbeautydev.com'; color = '🔵blue'; } const site = new Site(); console.log(site.url); // codingbeautydev.com console.log(color); // 🔵blue

Private methods and fields

Creating private methods was impossible before ES13.

We also had to the ugly underscore hack to indicate privacy — but that was just an indication.

❌ Before:

JavaScript
class Person { _firstName = 'Tari'; _lastName = 'Ibaba'; get name() { return `${this._firstName} ${this._lastName}`; } } const person = new Person(); console.log(person.name); // Tari Ibaba // We can still access private members! console.log(person._firstName); // Tari console.log(person._lastName); // Ibaba // They can also be modified person._firstName = 'Lionel'; person._lastName = 'Messi'; console.log(person.name); // Lionel Messi

✅ Now after ES13:

We can add private fields and members to a class by prefixing it with a hashtag (#):

You’ll get a syntax error if you try to access it from outside the class:

JavaScript
class Person { #firstName = 'Tari'; #lastName = 'Ibaba'; get name() { return `${this.#firstName} ${this.#lastName}`; } } const person = new Person(); console.log(person.name); // SyntaxError: Private field '#firstName' must be // declared in an enclosing class console.log(person.#firstName); console.log(person.#lastName);

We can see something interesting from the error message:

The compiler doesn’t expect you to even try to access private fields from outside the class — it assumes you’re trying to creating one.

Static class fields and static private methods

Static fields — properties of the class itself rather than any specific instance.

Ever since ES13 we can now easily create them for any class:

JavaScript
class Person { static #count = 0; static eyeCount = 2; static getCount() { // Access fellow static member with this return this.#count; } // instance member constructor() { // Access static member with this.constructor this.constructor.#incrementCount(); } static #incrementCount() { this.#count++; } } const person1 = new Person(); const person2 = new Person(); console.log(Person.getCount()); // 2

3. Array upgrades: new at() method

So usually we’d use square brackets ([]) to access the Nth of the array.

JavaScript
const arr = ['a', 'b', 'c', 'd']; console.log(arr[1]); // b

But accessing the Nth item from the end was always a pain — we had to index with arr.length - N:

❌ Before ES13:

JavaScript
const arr = ['a', 'b', 'c', 'd']; // 1st element from the end console.log(arr[arr.length - 1]); // d // 2nd element from the end console.log(arr[arr.length - 2]); // c

Luckily ES13 brought a new at() method that solved all that:

JavaScript
const str = 'Coding Beauty'; console.log(str.at(-1)); // y console.log(str.at(-2)); // t

4. Static class blocks

With static fields came static blocks.

To execute code only once, at the creation of the class — just like static constructors in OOP languages like C# and Java.

So you can create as many of them as you want in the class — all the code will run in the order you defined them:

JavaScript
class Vehicle { static defaultColor = 'blue'; } class Car extends Vehicle { static colors = []; static { this.colors.push(super.defaultColor, 'red'); } static { this.colors.push('green'); } } console.log(Car.colors); // [ 'blue', 'red', 'green' ]

5. Error reporting upgrades

So sometimes we catch errors of methods down the call stack only to rethrow it back up the stack.

But when we do this we lose crucial information from the original error:

JavaScript
try { userAction(); } catch (err) { // ❌ doesn't know fundamental cause of error console.log(err); } function userAction() { try { apiCallThatCanThrow(); } catch (err) { // 👇 rethrow throw new Error('New error message'); } } function apiCallThatCanThrow() { console.log('fetching from codingbeautydev.com...'); throw new Error('throwing for no reason'); }

That was why ES13 introduce a new cause property to preserve this important info and make debugging easier:

JavaScript
try { userAction(); } catch (err) { // ✅ now knows what caused the error console.log(err); console.log(`Cause by: ${err.cause}`); } function userAction() { try { apiCallThatCanThrow(); } catch (err) { // ✅ error cause throw new Error('New error message', { cause: err }); } } function apiCallThatCanThrow() { console.log('fetching from codingbeautydev.com...'); throw new Error('throwing for no reason'); }

Final thoughts

Overall ES13 was a significant leap for JavaScript with several features that have become essential for modern development.

Empowering you to write cleaner code with greater conciseness, expressiveness, and clarity.



Every Crazy Thing JavaScript Does

A captivating guide to the subtle caveats and lesser-known parts of JavaScript.

Every Crazy Thing JavaScript Does

Sign up and receive a free copy immediately.

Leave a Comment

Your email address will not be published. Required fields are marked *