modern-javascript-patterns
upstream

Master ES6+ features including async/await, destructuring, spread operators, arrow functions, promises, modules, iterators, generators, and functional programming patterns for writing clean, efficient JavaScript code. Use when refactoring legacy code, implementing modern patterns, or optimizing JavaScript applications.

ID: modern-javascript-patterns
Preview
Codigo

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:

``javascript

// 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:

`javascript

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:

`javascript

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:

`javascript

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:

`javascript

// 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:

`javascript

// 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


`javascript

// 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 = highlightName: ${name}, Age: ${age};

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

`


5. Enhanced Object Literals


`javascript

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:

`javascript

// 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:

`javascript

// 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:

`javascript

// 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:

`javascript

// 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


1. Array Methods


Map, Filter, Reduce:

`javascript

const users = [

{ id: 1, name: 'John', age: 30, active: true },

{ id: 2, name: 'Jane', age: 25, active: false },

{ id: 3, name: 'Bob', age: 35, active: true }

];


// Map - Transform array

const names = users.map(user => user.name);

const upperNames = users.map(user => user.name.toUpperCase());


// Filter - Select elements

const activeUsers = users.filter(user => user.active);

const adults = users.filter(user => user.age >= 18);


// Reduce - Aggregate data

const totalAge = users.reduce((sum, user) => sum + user.age, 0);

const avgAge = totalAge / users.length;


// Group by property

const byActive = users.reduce((groups, user) => {

const key = user.active ? 'active' : 'inactive';

return {

...groups,

[key]: [...(groups[key] || []), user]

};

}, {});


// Chaining methods

const result = users

.filter(user => user.active)

.map(user => user.name)

.sort()

.join(', ');

`


Advanced Array Methods:

`javascript

// Find - First matching element

const user = users.find(u => u.id === 2);


// FindIndex - Index of first match

const index = users.findIndex(u => u.name === 'Jane');


// Some - At least one matches

const hasActive = users.some(u => u.active);


// Every - All match

const allAdults = users.every(u => u.age >= 18);


// FlatMap - Map and flatten

const userTags = [

{ name: 'John', tags: ['admin', 'user'] },

{ name: 'Jane', tags: ['user'] }

];

const allTags = userTags.flatMap(u => u.tags);


// From - Create array from iterable

const str = 'hello';

const chars = Array.from(str);

const numbers = Array.from({ length: 5 }, (_, i) => i + 1);


// Of - Create array from arguments

const arr = Array.of(1, 2, 3);

`


2. Higher-Order Functions


Functions as Arguments:

`javascript

// Custom forEach

function forEach(array, callback) {

for (let i = 0; i < array.length; i++) {

callback(array[i], i, array);

}

}


// Custom map

function map(array, transform) {

const result = [];

for (const item of array) {

result.push(transform(item));

}

return result;

}


// Custom filter

function filter(array, predicate) {

const result = [];

for (const item of array) {

if (predicate(item)) {

result.push(item);

}

}

return result;

}

`


Functions Returning Functions:

`javascript

// Currying

const multiply = a => b => a * b;

const double = multiply(2);

const triple = multiply(3);


console.log(double(5)); // 10

console.log(triple(5)); // 15


// Partial application

function partial(fn, ...args) {

return (...moreArgs) => fn(...args, ...moreArgs);

}


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

const add5 = partial(add, 5);

console.log(add5(3, 2)); // 10


// Memoization

function 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);

});

`


3. Composition and Piping


`javascript

// Function composition

const compose = (...fns) => x =>

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


const pipe = (...fns) => x =>

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


// Example usage

const addOne = x => x + 1;

const double = x => x * 2;

const square = x => x * x;


const composed = compose(square, double, addOne);

console.log(composed(3)); // ((3 + 1) * 2)^2 = 64


const piped = pipe(addOne, double, square);

console.log(piped(3)); // ((3 + 1) * 2)^2 = 64


// Practical example

const processUser = pipe(

user => ({ ...user, name: user.name.trim() }),

user => ({ ...user, email: user.email.toLowerCase() }),

user => ({ ...user, age: parseInt(user.age) })

);


const user = processUser({

name: ' John ',

email: 'JOHN@EXAMPLE.COM',

age: '30'

});

`


4. Pure Functions and Immutability


`javascript

// Impure function (modifies input)

function addItemImpure(cart, item) {

cart.items.push(item);

cart.total += item.price;

return cart;

}


// Pure function (no side effects)

function addItemPure(cart, item) {

return {

...cart,

items: [...cart.items, item],

total: cart.total + item.price

};

}


// Immutable array operations

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


// Add to array

const withSix = [...numbers, 6];


// Remove from array

const withoutThree = numbers.filter(n => n !== 3);


// Update array element

const doubled = numbers.map(n => n === 3 ? n * 2 : n);


// Immutable object operations

const user = { name: 'John', age: 30 };


// Update property

const olderUser = { ...user, age: 31 };


// Add property

const withEmail = { ...user, email: 'john@example.com' };


// Remove property

const { age, ...withoutAge } = user;


// Deep cloning (simple approach)

const deepClone = obj => JSON.parse(JSON.stringify(obj));


// Better deep cloning

const structuredClone = obj => globalThis.structuredClone(obj);

`


Modern Class Features


`javascript

// Class syntax

class User {

// Private fields

#password;


// Public fields

id;

name;


// Static field

static count = 0;


constructor(id, name, password) {

this.id = id;

this.name = name;

this.#password = password;

User.count++;

}


// Public method

greet() {

return Hello, ${this.name};

}


// Private method

#hashPassword(password) {

return hashed_${password};

}


// Getter

get displayName() {

return this.name.toUpperCase();

}


// Setter

set password(newPassword) {

this.#password = this.#hashPassword(newPassword);

}


// Static method

static create(id, name, password) {

return new User(id, name, password);

}

}


// Inheritance

class Admin extends User {

constructor(id, name, password, role) {

super(id, name, password);

this.role = role;

}


greet() {

return ${super.greet()}, I'm an admin;

}

}

`


Modules (ES6)


`javascript

// Exporting

// math.js

export const PI = 3.14159;

export function add(a, b) {

return a + b;

}

export class Calculator {

// ...

}


// Default export

export default function multiply(a, b) {

return a * b;

}


// Importing

// app.js

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


// Rename imports

import { add as sum } from './math.js';


// Import all

import * as Math from './math.js';


// Dynamic imports

const module = await import('./math.js');

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


// Conditional loading

if (condition) {

const module = await import('./feature.js');

module.init();

}

`


Iterators and Generators


`javascript

// Custom iterator

const range = {

from: 1,

to: 5,


[Symbol.iterator]() {

return {

current: this.from,

last: this.to,


next() {

if (this.current <= this.last) {

return { done: false, value: this.current++ };

} else {

return { done: true };

}

}

};

}

};


for (const num of range) {

console.log(num); // 1, 2, 3, 4, 5

}


// Generator function

function* rangeGenerator(from, to) {

for (let i = from; i <= to; i++) {

yield i;

}

}


for (const num of rangeGenerator(1, 5)) {

console.log(num);

}


// Infinite generator

function* fibonacci() {

let [prev, curr] = [0, 1];

while (true) {

yield curr;

[prev, curr] = [curr, prev + curr];

}

}


// Async generator

async function* fetchPages(url) {

let page = 1;

while (true) {

const response = await fetch(${url}?page=${page});

const data = await response.json();

if (data.length === 0) break;

yield data;

page++;

}

}


for await (const page of fetchPages('/api/users')) {

console.log(page);

}

`


Modern Operators


`javascript

// Optional chaining

const user = { name: 'John', address: { city: 'NYC' } };

const city = user?.address?.city;

const zipCode = user?.address?.zipCode; // undefined


// Function call

const result = obj.method?.();


// Array access

const first = arr?.[0];


// Nullish coalescing

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

const value = undefined ?? 'default'; // 'default'

const value = 0 ?? 'default'; // 0 (not 'default')

const value = '' ?? 'default'; // '' (not 'default')


// Logical assignment

let a = null;

a ??= 'default'; // a = 'default'


let b = 5;

b ??= 10; // b = 5 (unchanged)


let obj = { count: 0 };

obj.count ||= 1; // obj.count = 1

obj.count &&= 2; // obj.count = 2

`


Performance Optimization


`javascript

// Debounce

function debounce(fn, delay) {

let timeoutId;

return (...args) => {

clearTimeout(timeoutId);

timeoutId = setTimeout(() => fn(...args), delay);

};

}


const searchDebounced = debounce(search, 300);


// Throttle

function throttle(fn, limit) {

let inThrottle;

return (...args) => {

if (!inThrottle) {

fn(...args);

inThrottle = true;

setTimeout(() => inThrottle = false, limit);

}

};

}


const scrollThrottled = throttle(handleScroll, 100);


// Lazy evaluation

function* lazyMap(iterable, transform) {

for (const item of iterable) {

yield transform(item);

}

}


// Use only what you need

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

const doubled = lazyMap(numbers, x => x * 2);

const first = doubled.next().value; // Only computes first value

`


Best Practices


1. Use const by default: Only use let when reassignment is needed

2. Prefer arrow functions: Especially for callbacks

3. Use template literals: Instead of string concatenation

4. Destructure objects and arrays: For cleaner code

5. Use async/await: Instead of Promise chains

6. Avoid mutating data: Use spread operator and array methods

7. Use optional chaining: Prevent "Cannot read property of undefined"

8. Use nullish coalescing: For default values

9. Prefer array methods: Over traditional loops

10. Use modules: For better code organization

11. Write pure functions: Easier to test and reason about

12. Use meaningful variable names: Self-documenting code

13. Keep functions small: Single responsibility principle

14. Handle errors properly: Use try/catch with async/await

15. Use strict mode: 'use strict'` for better error catching


Common Pitfalls


1. this binding confusion: Use arrow functions or bind()

2. Async/await without error handling: Always use try/catch

3. Promise creation unnecessary: Don't wrap already async functions

4. Mutation of objects: Use spread operator or Object.assign()

5. Forgetting await: Async functions return promises

6. Blocking event loop: Avoid synchronous operations

7. Memory leaks: Clean up event listeners and timers

8. Not handling promise rejections: Use catch() or try/catch


Resources


  • MDN Web Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript
  • JavaScript.info: https://javascript.info/
  • You Don't Know JS: https://github.com/getify/You-Dont-Know-JS
  • Eloquent JavaScript: https://eloquentjavascript.net/
  • ES6 Features: http://es6-features.org/

Informacion

Estado
Activo
Origen upstream
Usos 3
Ultimo uso 23/01 16:57
Actualizado 09/01/2026 02:14

Archivo origen

Z:\Repositorios\core\upstream\plugins\javascript-typescript\skills\modern-javascript-patterns\SKILL.md