modern-javascript-patterns

ES6+ syntax and functional programming patterns for writing clean, modern JavaScript. Master arrow functions, destructuring, spread operators, template literals, and enhanced object syntax for concise, readable code Implement async/await and Promise patterns for handling asynchronous operations, with combinators like Promise.all and Promise.race Apply functional programming techniques including map, filter, reduce, higher-order functions, composition, and pure functions for data transformation Use modern features like optional chaining, nullish coalescing, generators, iterators, and modules for robust, maintainable applications Optimize performance with debouncing, throttling, memoization, and lazy evaluation patterns

INSTALLATION
npx skills add https://github.com/wshobson/agents --skill modern-javascript-patterns
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

Modern JavaScript Patterns

Comprehensive guide for mastering modern JavaScript (ES6+) features, functional programming patterns, and best practices for writing clean, maintainable, and performant code.

When to Use This Skill

  • Refactoring legacy JavaScript to modern syntax
  • Implementing functional programming patterns
  • Optimizing JavaScript performance
  • Writing maintainable and readable code
  • Working with asynchronous operations
  • Building modern web applications
  • Migrating from callbacks to Promises/async-await
  • Implementing data transformation pipelines

ES6+ Core Features

1. Arrow Functions

Syntax and Use Cases:

// Traditional function

function add(a, b) {

  return a + b;

}

// Arrow function

const add = (a, b) => a + b;

// Single parameter (parentheses optional)

const double = (x) => x * 2;

// No parameters

const getRandom = () => Math.random();

// Multiple statements (need curly braces)

const processUser = (user) => {

  const normalized = user.name.toLowerCase();

  return { ...user, name: normalized };

};

// Returning objects (wrap in parentheses)

const createUser = (name, age) => ({ name, age });

Lexical 'this' Binding:

class Counter {

  constructor() {

    this.count = 0;

  }

  // Arrow function preserves 'this' context

  increment = () => {

    this.count++;

  };

  // Traditional function loses 'this' in callbacks

  incrementTraditional() {

    setTimeout(function () {

      this.count++; // 'this' is undefined

    }, 1000);

  }

  // Arrow function maintains 'this'

  incrementArrow() {

    setTimeout(() => {

      this.count++; // 'this' refers to Counter instance

    }, 1000);

  }

}

2. Destructuring

Object Destructuring:

const user = {

  id: 1,

  name: "John Doe",

  email: "john@example.com",

  address: {

    city: "New York",

    country: "USA",

  },

};

// Basic destructuring

const { name, email } = user;

// Rename variables

const { name: userName, email: userEmail } = user;

// Default values

const { age = 25 } = user;

// Nested destructuring

const {

  address: { city, country },

} = user;

// Rest operator

const { id, ...userWithoutId } = user;

// Function parameters

function greet({ name, age = 18 }) {

  console.log(`Hello ${name}, you are ${age}`);

}

greet(user);

Array Destructuring:

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

// Basic destructuring

const [first, second] = numbers;

// Skip elements

const [, , third] = numbers;

// Rest operator

const [head, ...tail] = numbers;

// Swapping variables

let a = 1,

  b = 2;

[a, b] = [b, a];

// Function return values

function getCoordinates() {

  return [10, 20];

}

const [x, y] = getCoordinates();

// Default values

const [one, two, three = 0] = [1, 2];

3. Spread and Rest Operators

Spread Operator:

// Array spreading

const arr1 = [1, 2, 3];

const arr2 = [4, 5, 6];

const combined = [...arr1, ...arr2];

// Object spreading

const defaults = { theme: "dark", lang: "en" };

const userPrefs = { theme: "light" };

const settings = { ...defaults, ...userPrefs };

// Function arguments

const numbers = [1, 2, 3];

Math.max(...numbers);

// Copying arrays/objects (shallow copy)

const copy = [...arr1];

const objCopy = { ...user };

// Adding items immutably

const newArr = [...arr1, 4, 5];

const newObj = { ...user, age: 30 };

Rest Parameters:

// Collect function arguments

function sum(...numbers) {

  return numbers.reduce((total, num) => total + num, 0);

}

sum(1, 2, 3, 4, 5);

// With regular parameters

function greet(greeting, ...names) {

  return `${greeting} ${names.join(", ")}`;

}

greet("Hello", "John", "Jane", "Bob");

// Object rest

const { id, ...userData } = user;

// Array rest

const [first, ...rest] = [1, 2, 3, 4, 5];

4. Template Literals

// Basic usage

const name = "John";

const greeting = `Hello, ${name}!`;

// Multi-line strings

const html = `

  <div>

    <h1>${title}</h1>

    <p>${content}</p>

  </div>

`;

// Expression evaluation

const price = 19.99;

const total = `Total: $${(price * 1.2).toFixed(2)}`;

// Tagged template literals

function highlight(strings, ...values) {

  return strings.reduce((result, str, i) => {

    const value = values[i] || "";

    return result + str + `<mark>${value}</mark>`;

  }, "");

}

const name = "John";

const age = 30;

const html = highlight`Name: ${name}, Age: ${age}`;

// Output: "Name: <mark>John</mark>, Age: <mark>30</mark>"

5. Enhanced Object Literals

const name = "John";

const age = 30;

// Shorthand property names

const user = { name, age };

// Shorthand method names

const calculator = {

  add(a, b) {

    return a + b;

  },

  subtract(a, b) {

    return a - b;

  },

};

// Computed property names

const field = "email";

const user = {

  name: "John",

  [field]: "john@example.com",

  [`get${field.charAt(0).toUpperCase()}${field.slice(1)}`]() {

    return this[field];

  },

};

// Dynamic property creation

const createUser = (name, ...props) => {

  return props.reduce(

    (user, [key, value]) => ({

      ...user,

      [key]: value,

    }),

    { name },

  );

};

const user = createUser("John", ["age", 30], ["email", "john@example.com"]);

Asynchronous Patterns

1. Promises

Creating and Using Promises:

// Creating a promise

const fetchUser = (id) => {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

      if (id > 0) {

        resolve({ id, name: "John" });

      } else {

        reject(new Error("Invalid ID"));

      }

    }, 1000);

  });

};

// Using promises

fetchUser(1)

  .then((user) => console.log(user))

  .catch((error) => console.error(error))

  .finally(() => console.log("Done"));

// Chaining promises

fetchUser(1)

  .then((user) => fetchUserPosts(user.id))

  .then((posts) => processPosts(posts))

  .then((result) => console.log(result))

  .catch((error) => console.error(error));

Promise Combinators:

// Promise.all - Wait for all promises

const promises = [fetchUser(1), fetchUser(2), fetchUser(3)];

Promise.all(promises)

  .then((users) => console.log(users))

  .catch((error) => console.error("At least one failed:", error));

// Promise.allSettled - Wait for all, regardless of outcome

Promise.allSettled(promises).then((results) => {

  results.forEach((result) => {

    if (result.status === "fulfilled") {

      console.log("Success:", result.value);

    } else {

      console.log("Error:", result.reason);

    }

  });

});

// Promise.race - First to complete

Promise.race(promises)

  .then((winner) => console.log("First:", winner))

  .catch((error) => console.error(error));

// Promise.any - First to succeed

Promise.any(promises)

  .then((first) => console.log("First success:", first))

  .catch((error) => console.error("All failed:", error));

2. Async/Await

Basic Usage:

// Async function always returns a Promise

async function fetchUser(id) {

  const response = await fetch(`/api/users/${id}`);

  const user = await response.json();

  return user;

}

// Error handling with try/catch

async function getUserData(id) {

  try {

    const user = await fetchUser(id);

    const posts = await fetchUserPosts(user.id);

    return { user, posts };

  } catch (error) {

    console.error("Error fetching data:", error);

    throw error;

  }

}

// Sequential vs Parallel execution

async function sequential() {

  const user1 = await fetchUser(1); // Wait

  const user2 = await fetchUser(2); // Then wait

  return [user1, user2];

}

async function parallel() {

  const [user1, user2] = await Promise.all([fetchUser(1), fetchUser(2)]);

  return [user1, user2];

}

Advanced Patterns:

// Async IIFE

(async () => {

  const result = await someAsyncOperation();

  console.log(result);

})();

// Async iteration

async function processUsers(userIds) {

  for (const id of userIds) {

    const user = await fetchUser(id);

    await processUser(user);

  }

}

// Top-level await (ES2022)

const config = await fetch("/config.json").then((r) => r.json());

// Retry logic

async function fetchWithRetry(url, retries = 3) {

  for (let i = 0; i < retries; i++) {

    try {

      return await fetch(url);

    } catch (error) {

      if (i === retries - 1) throw error;

      await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));

    }

  }

}

// Timeout wrapper

async function withTimeout(promise, ms) {

  const timeout = new Promise((_, reject) =>

    setTimeout(() => reject(new Error("Timeout")), ms),

  );

  return Promise.race([promise, timeout]);

}

Functional Programming Patterns

Functional programming in JavaScript centers on pure functions, immutability, and composable transformations.

Key topics covered in references/advanced-patterns.md:

  • Array methodsmap, filter, reduce, find, findIndex, some, every, flatMap, Array.from
  • Higher-order functions — custom forEach/map/filter, currying, partial application, memoization
  • Composition and pipingcompose/pipe utilities with practical data transformation examples
  • Pure functions and immutability — immutable array/object operations, deep cloning with structuredClone

Modern Class Features

ES2022 classes support private fields (#field), static fields, getters/setters, and private methods. See references/advanced-patterns.md for a full example with inheritance.

Modules (ES6)

// Named exports

export const PI = 3.14159;

export function add(a, b) { return a + b; }

// Default export

export default function multiply(a, b) { return a * b; }

// Import

import multiply, { PI, add } from "./math.js";

// Dynamic import (code splitting)

const { add } = await import("./math.js");

For re-exports, namespace imports, and conditional dynamic loading see references/advanced-patterns.md.

Iterators and Generators

Generators (function*) and async generators (async function*) enable lazy sequences and async pagination. See references/advanced-patterns.md for custom iterator, range generator, fibonacci, and for await...of examples.

Modern Operators

// Optional chaining — safe property access

const city = user?.address?.city;

const result = obj.method?.();

// Nullish coalescing — default only for null/undefined (not 0 or "")

const value = null ?? "default"; // 'default'

const zero = 0 ?? "default";    // 0

// Logical assignment

a ??= "default";   // assign if null/undefined

obj.count ||= 1;   // assign if falsy

obj.count &#x26;&#x26;= 2;   // assign if truthy

Performance Optimization

See references/advanced-patterns.md for debounce, throttle, and lazy evaluation with generators.

Best Practices

  • Use const by default: Only use let when reassignment is needed
  • Prefer arrow functions: Especially for callbacks
  • Use template literals: Instead of string concatenation
  • Destructure objects and arrays: For cleaner code
  • Use async/await: Instead of Promise chains
  • Avoid mutating data: Use spread operator and array methods
  • Use optional chaining: Prevent "Cannot read property of undefined"
  • Use nullish coalescing: For default values
  • Prefer array methods: Over traditional loops
  • Use modules: For better code organization
  • Write pure functions: Easier to test and reason about
  • Use meaningful variable names: Self-documenting code
  • Keep functions small: Single responsibility principle
  • Handle errors properly: Use try/catch with async/await
  • Use strict mode: 'use strict' for better error catching

For common pitfalls (this binding, promise anti-patterns, memory leaks), see references/advanced-patterns.md.

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card