Modern JavaScript: Beyond the Basics

A deep dive into modern JavaScript features that go beyond tutorials—the techniques and patterns that separate intermediate from advanced developers.

• 10 min read
JavaScriptWeb DevelopmentAdvancedES2024

Modern JavaScript: Beyond the Basics

JavaScript has evolved tremendously over the past few years. Let’s explore some advanced features and patterns that can level up your code.

Advanced Async Patterns

Promise Combinators

Beyond Promise.all, modern JavaScript offers several combinators:

// Promise.allSettled - Wait for all, regardless of success/failure
const results = await Promise.allSettled([
  fetch('/api/user'),
  fetch('/api/posts'),
  fetch('/api/comments'),
]);

// Promise.race - First to finish wins
const fastest = await Promise.race([
  fetch('/api/server1'),
  fetch('/api/server2'),
]);

// Promise.any - First success wins (ignores failures)
const firstSuccess = await Promise.any([
  fetchFromCDN1(),
  fetchFromCDN2(),
  fetchFromCDN3(),
]);

Async Iteration

async function* generateData() {
  for (let i = 0; i < 5; i++) {
    await delay(1000);
    yield i;
  }
}

// Consume with for-await-of
for await (const value of generateData()) {
  console.log(value); // 0, 1, 2, 3, 4 (with 1s delays)
}

Advanced Object Patterns

Proxy for Validation

const createValidatedObject = (schema) => {
  return new Proxy(
    {},
    {
      set(target, property, value) {
        const validator = schema[property];
        if (!validator || validator(value)) {
          target[property] = value;
          return true;
        }
        throw new Error(`Invalid value for ${property}`);
      },
    }
  );
};

const user = createValidatedObject({
  age: (val) => typeof val === 'number' && val > 0,
  email: (val) => /\S+@\S+\.\S+/.test(val),
});

user.age = 25; // OK
user.email = '[email protected]'; // OK
user.age = -5; // Throws error

Private Fields

class BankAccount {
  #balance = 0; // Private field

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

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

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100
console.log(account.#balance); // SyntaxError

Functional Patterns

Composition and Pipe

const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((acc, fn) => fn(acc), x);

const pipe =
  (...fns) =>
  (x) =>
    fns.reduce((acc, fn) => fn(acc), x);

// Usage
const addOne = (x) => x + 1;
const double = (x) => x * 2;
const square = (x) => x * x;

const calculate = pipe(
  addOne, // 6
  double, // 12
  square // 144
);

console.log(calculate(5)); // 144

Currying

const curry = (fn) => {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return (...nextArgs) => curried(...args, ...nextArgs);
  };
};

const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);

console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6

Performance Optimization

Memoization

const memoize = (fn) => {
  const cache = new Map();

  return (...args) => {
    const key = JSON.stringify(args);

    if (cache.has(key)) {
      return cache.get(key);
    }

    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
};

const fibonacci = memoize((n) => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
});

WeakMap for Private Data

const privateData = new WeakMap();

class User {
  constructor(name, password) {
    this.name = name;
    privateData.set(this, { password });
  }

  verifyPassword(input) {
    return privateData.get(this).password === input;
  }
}

Modern Tooling Integration

Optional Chaining and Nullish Coalescing

// Optional chaining
const userName = user?.profile?.name ?? 'Guest';

// Nullish coalescing (only null/undefined, not falsy)
const count = response.count ?? 0; // 0 if null/undefined
const isActive = response.isActive ?? true; // true if null/undefined

Top-Level Await (ES2022)

// In modules, you can use await at the top level
const data = await fetch('/api/config').then((r) => r.json());

export default data;

Conclusion

Modern JavaScript is incredibly powerful. These patterns and features allow you to write more expressive, maintainable, and performant code.

The key is knowing when to use them—advanced techniques should solve problems, not create complexity for complexity’s sake.

Keep learning, keep experimenting, and most importantly, keep building!