javascript-development
SKILL.md
JavaScript Development
Expert guidance for writing modern JavaScript code with ES2024+ features, async programming patterns, DOM manipulation, API integration, and best practices following official JavaScript resources at https://developer.mozilla.org/en-US/docs/Web/JavaScript.
Skill Paths
- Workspace skills:
.github/skills/ - Global skills:
C:/Users/LOQ/.agents/skills/
Activation Conditions
Core JavaScript Development:
- Writing modern JavaScript with ES2024+ features
- Creating React components and hooks
- Working with DOM manipulation and events
- Implementing forms and user interactions
- Managing state in frontend applications
Asynchronous Programming:
- Using Promises and async/await patterns
- Fetching data from APIs
- Handling loading and error states
- Implementing retry mechanisms
- Working with concurrent operations
Data Handling:
- Manipulating arrays and objects
- Using modern array methods (map, filter, reduce, find)
- Working with JSON data
- LocalStorage and session management
- Data transformation and formatting
API Integration:
- Fetch API for HTTP requests
- Axios for advanced HTTP client features
- RESTful API design and consumption
- Authentication with JWT tokens
- CORS and error handling
Part 1: Modern JavaScript (ES2024+)
New Features & Syntax
// Logical Assignment Operators (ES2021)
const obj = { a: 1 };
obj.a ??= 10; // Only assign if obj.a is null/undefined
console.log(obj.a); // 1 (no change)
obj.b ??= 20; // Assign if missing
console.log(obj.b); // 20
// Numeric Separators (ES2021)
const billion = 1_000_000_000;
const bytes = 0xff_13_ff; // Hex
// String methods (ES2022)
const str = "Hello World";
console.log(str.replaceAll('l', 'L')); // "HeLLo WorLd"
console.log(str.at(-1)); // "d" (last character)
// Array methods (ES2023)
const array = [1, 2, 3, 4, 5];
console.log(array.toReversed()); // [5, 4, 3, 2, 1]
console.log(array.toSorted()); // [1, 2, 3, 4, 5]
console.log(array.with(2, 99)); // [1, 2, 99, 4, 5] (non-mutating)
// Hashbang (for scripts)
#!/usr/bin/env node
console.log("Executable script!");
// Object.groupBy (ES2024)
const people = [
{ name: "Alice", age: 25, role: "admin" },
{ name: "Bob", age: 30, role: "user" },
{ name: "Charlie", age: 25, role: "user" },
];
const groupedByAge = Object.groupBy(people, ({ age }) => age);
console.log(groupedByAge);
// { 25: [{name: "Alice", age: 25, role: "admin"}, ...], 30: [...] }
const groupedByRole = Map.groupBy(people, ({ role }) => role);
console.log(groupedByRole);
// Map { "admin" => [...], "user" => [...] }
Template Literals & Tagged Templates
// Template literals with expressions
const firstName = "John";
const lastName = "Doe";
const greeting = `Hello, ${firstName} ${lastName}!`;
// Tagged template for custom formatting
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] ? `<strong>${values[i]}</strong>` : '');
}, '');
}
const message = highlight`User ${firstName} is online.`;
// "User <strong>John</strong> is online."
Destructuring & Spread
// Object destructuring
const user = { id: 1, name: "Alice", email: "alice@example.com", role: "admin" };
const { name, email, role: userRole } = user;
console.log(name, email, userRole); // "Alice", "alice@example.com", "admin"
// Array destructuring
const numbers = [1, 2, 3, 4, 5];
const [first, second, ...rest] = numbers;
console.log(first, second, rest); // 1, 2, [3, 4, 5]
// Destructuring in function parameters
function processRecipe({ title, difficulty, ingredients = [] }) {
return `${title} (${difficulty}) - ${ingredients.length} ingredients`;
}
const recipe = { title: "Pasta", difficulty: "Medium", ingredients: ["Pasta", "Sauce"] };
processRecipe(recipe); // "Pasta (Medium) - 2 ingredients"
Part 2: Async Programming
Async/Await Patterns
// Basic async/await
async function fetchUser(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const user = await response.json();
return user;
} catch (error) {
console.error("Failed to fetch user:", error);
throw error;
}
}
// Parallel async operations with Promise.all
async function fetchRecipeData(recipeId) {
try {
const [recipe, reviews, ingredients] = await Promise.all([
fetch(`/api/recipes/${recipeId}`).then(r => r.json()),
fetch(`/api/recipes/${recipeId}/reviews`).then(r => r.json()),
fetch(`/api/recipes/${recipeId}/ingredients`).then(r => r.json()),
]);
return { recipe, reviews, ingredients };
} catch (error) {
console.error("Failed to fetch recipe data:", error);
throw error;
}
}
// Race with Promise.any (ES2021)
async function fetchFromMultipleEndpoints() {
const endpoints = [
'/api/v1/users',
'/api/v2/users',
'/api/v3/users',
];
try {
const response = await Promise.any(
endpoints.map(url => fetch(url).then(r => r.json()))
);
return response;
} catch (error) {
// All promises rejected
console.error("All endpoints failed:", error);
throw error;
}
}
// All Settled (ES2020)
async function fetchWithStatus() {
const requests = [
fetch('/api/users'),
fetch('/api/recipes'),
fetch('/api/stats'),
];
const results = await Promise.allSettled(requests);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Request ${index} successful`);
} else {
console.error(`Request ${index} failed:`, result.reason);
}
});
}
Async Iterator (ES2018)
// Async generator function
async function* fetchPaginatedUsers(pageSize = 10) {
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(`/api/users?page=${page}&limit=${pageSize}`);
const { users, totalPages } = await response.json();
yield* users;
page++;
hasMore = page <= totalPages;
// Add delay to avoid rate limiting
await new Promise(resolve => setTimeout(resolve, 500));
}
}
// Using async iterator
async function getAllUsers() {
const userIterator = fetchPaginatedUsers();
const allUsers = [];
for await (const user of userIterator) {
allUsers.push(user);
console.log(`Fetched: ${user.name}`);
}
return allUsers;
}
Top-level Await (ES2022)
// In ES modules, you can use await at top level
const config = await fetch('/api/config').then(r => r.json());
console.log('App config loaded:', config);
// This is useful for modules that need to load data before export
export const recipes = await fetch('/api/recipes').then(r => r.json());
export const users = await fetch('/api/users').then(r => r.json());
Part 3: API Integration
Fetch API Patterns
// Basic GET request
async function getRecipes(filters = {}) {
const queryParams = new URLSearchParams(filters);
const url = `/api/recipes?${queryParams}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return await response.json();
}
// POST request with JSON body
async function createRecipe(recipeData) {
const response = await fetch('/api/recipes', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(recipeData),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Failed to create recipe');
}
return await response.json();
}
// PUT request with authentication
async function updateRecipe(recipeId, recipeData, token) {
const response = await fetch(`/api/recipes/${recipeId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(recipeData),
});
if (!response.status === 200 && response.status !== 204) {
throw new Error(`Update failed: ${response.status}`);
}
return await response.json();
}
// DELETE request
async function deleteRecipe(recipeId, token) {
const response = await fetch(`/api/recipes/${recipeId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Delete failed: ${response.status}`);
}
return true;
}
Axios Integration
import axios from 'axios';
// Create axios instance with default config
const api = axios.create({
baseURL: '/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor for adding authentication
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor for error handling
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Unauthorized - redirect to login
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// API methods
export const recipesApi = {
async getAll(filters = {}) {
const response = await api.get('/recipes', { params: filters });
return response.data;
},
async getById(recipeId) {
const response = await api.get(`/recipes/${recipeId}`);
return response.data;
},
async create(recipeData) {
const response = await api.post('/recipes', recipeData);
return response.data;
},
async update(recipeId, recipeData) {
const response = await api.put(`/recipes/${recipeId}`, recipeData);
return response.data;
},
async delete(recipeId) {
const response = await api.delete(`/recipes/${recipeId}`);
return response.data;
},
};
Error Handling & Retry
// Retry utility with exponential backoff
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
} catch (error) {
lastError = error;
console.log(`Attempt ${attempt + 1} failed, retrying...`);
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);
}
// Usage with error handling
async function getRecipeWithRetry(recipeId) {
try {
const recipe = await fetchWithRetry(`/api/recipes/${recipeId}`);
console.log('Recipe loaded:', recipe);
return recipe;
} catch (error) {
console.error('Failed to load recipe:', error);
// Show user-friendly error message
return { error: true, message: 'Failed to load recipe. Please try again.' };
}
}
Part 4: DOM Manipulation & Events
Element Selection & Manipulation
// Modern element selection
const button = document.querySelector('#submit-btn');
const items = document.querySelectorAll('.list-item');
const container = document.getElementById('container');
const firstChild = document.querySelector('.item:first-child');
// Using closest for event delegation
document.addEventListener('click', (event) => {
const button = event.target.closest('.action-button');
if (button) {
console.log('Button clicked:', button.dataset.id);
}
});
// Element creation and manipulation
function addRecipeToList(recipe) {
const li = document.createElement('li');
li.className = 'recipe-item';
li.dataset.recipeId = recipe.id;
li.innerHTML = `
<h3>${recipe.title}</h3>
<p>${recipe.description}</p>
<button class="delete-btn">Delete</button>
`;
// Add event listener to delete button
const deleteBtn = li.querySelector('.delete-btn');
deleteBtn.addEventListener('click', () => deleteRecipe(recipe.id));
// Append to container
document.getElementById('recipe-list').appendChild(li);
}
// Element removal
function removeRecipeElement(recipeId) {
const element = document.querySelector(`[data-recipe-id="${recipeId}"]`);
if (element) {
element.remove();
}
}
Event Handling
// Form submission with validation
const form = document.getElementById('recipe-form');
form.addEventListener('submit', async (event) => {
event.preventDefault(); // Prevent default form submission
const formData = new FormData(form);
const recipeData = {
title: formData.get('title'),
description: formData.get('description'),
category: formData.get('category'),
difficulty: formData.get('difficulty'),
};
// Validation
if (!recipeData.title || recipeData.title.length < 3) {
showError('Title is required and must be at least 3 characters');
return;
}
try {
// Submit to API
const response = await fetch('/api/recipes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(recipeData),
});
if (response.ok) {
showSuccess('Recipe created successfully!');
form.reset();
}
} catch (error) {
showError('Failed to create recipe: ' + error.message);
}
});
// Debounce for search input
let searchTimeout;
const searchInput = document.getElementById('search');
searchInput.addEventListener('input', (event) => {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
const query = event.target.value;
if (query.length >= 2) {
performSearch(query);
}
}, 300); // Wait 300ms after user stops typing
});
// Intersection Observer for infinite scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadMoreRecipes();
}
});
}, {
root: null,
threshold: 0.1,
});
// Observe sentinel element
const sentinel = document.getElementById('load-more-sentinel');
observer.observe(sentinel);
Part 5: Data Structures & Algorithms
Array Methods
// map - Transform array
const recipes = [
{ title: 'Pasta', difficulty: 'Medium' },
{ title: 'Salad', difficulty: 'Easy' },
];
const titles = recipes.map(recipe => recipe.title);
// ['Pasta', 'Salad']
// filter - Select elements
const easyRecipes = recipes.filter(recipe => recipe.difficulty === 'Easy');
// [{ title: 'Salad', difficulty: 'Easy' }]
// reduce - Aggregate array
const wordCount = recipes.reduce((count, recipe) => {
return count + recipe.title.split(' ').length;
}, 0);
// find - Find first matching element
const pasta = recipes.find(recipe => recipe.title.includes('Pasta'));
// some - Check if any matches
const hasMediumDifficulty = recipes.some(recipe => recipe.difficulty === 'Medium'); // true
// every - Check if all match
const allHaveTitles = recipes.every(recipe => recipe.title.length > 0); // true
// sort - Sort array
const sortedRecipes = [...recipes].sort((a, b) =>
a.title.localeCompare(b.title)
);
// flatMap - Map and flatten
const nested = [[1, 2], [3, 4]];
const flattened = nested.flatMap(arr => arr); // [1, 2, 3, 4]
Object Methods
// Object.keys, Object.values, Object.entries
const user = {
id: 1,
name: 'Alice',
email: 'alice@example.com',
role: 'admin',
};
const keys = Object.keys(user); // ['id', 'name', 'email', 'role']
const values = Object.values(user); // [1, 'Alice', 'alice@example.com', 'admin']
const entries = Object.entries(user);
// [['id', 1], ['name', 'Alice'], ['email', 'alice@example.com'], ['role', 'admin']]
// Object.fromEntries - Convert back to object
const filtered = Object.fromEntries(
Object.entries(user).filter(([key]) => key !== 'role')
);
// { id: 1, name: 'Alice', email: 'alice@example.com' }
// Object.freeze - Make immutable
const config = Object.freeze({ apiUrl: '/api/v1' });
config.apiUrl = '/api/v2'; // Error in strict mode
Set and Map
// Set - Unique values
const tags = new Set(['Easy', 'Medium', 'Medium', 'Hard']);
console.log(tags.size); // 3
tags.add('Easy');
console.log(tags.has('Medium')); // true
tags.delete('Hard');
const uniqueTags = Array.from(tags); // ['Easy', 'Medium']
// Map - Key-value pairs with any type
const userRoles = new Map();
userRoles.set(1, 'admin');
userRoles.set(2, 'user');
userRoles.set('alice', 'editor');
console.log(userRoles.get(1)); // 'admin'
console.log(userRoles.has('alice')); // true
const roles = Array.from(userRoles.entries());
// [[1, 'admin'], [2, 'user'], ['alice', 'editor']]
Part 6: Date & Time
Date Operations
// Creating dates
const now = new Date();
const specificDate = new Date('2024-02-01');
const fromTimestamp = new Date(17068896000000);
// Date formatting
function formatDate(date) {
const options = {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
};
return new Intl.DateTimeFormat('en-US', options).format(date);
}
// "February 1, 2024 at 12:00 PM"
// Relative time (e.g., "2 hours ago")
function getRelativeTime(date) {
const now = new Date();
const diff = now - date;
const seconds = Math.floor(diff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (seconds < 60) return 'just now';
if (minutes < 60) return `${minutes} minute${minutes > 1 ? 's' : ''} ago`;
if (hours < 24) return `${hours} hour${hours > 1 ? 's' : ''} ago`;
return `${days} day${days > 1 ? 's' : ''} ago`;
}
// Date manipulation
function addDays(date, days) {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
function isSameDay(date1, date2) {
return date1.toDateString() === date2.toDateString();
}
Part 7: LocalStorage & State Management
LocalStorage Wrapper
class StorageManager {
static set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Failed to save to localStorage:', error);
}
}
static get(key, defaultValue = null) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.error('Failed to read from localStorage:', error);
return defaultValue;
}
}
static remove(key) {
try {
localStorage.removeItem(key);
} catch (error) {
console.error('Failed to remove from localStorage:', error);
}
}
static clear() {
try {
localStorage.clear();
} catch (error) {
console.error('Failed to clear localStorage:', error);
}
}
static has(key) {
return localStorage.getItem(key) !== null;
}
}
State Management (Simple)
class StateManager {
constructor(initialState = {}) {
this.state = initialState;
this.listeners = [];
}
setState(newState) {
this.state = { ...this.state, ...newState };
this.notify();
}
subscribe(listener) {
this.listeners.push(listener);
listener(this.state);
}
notify() {
this.listeners.forEach(listener => listener(this.state));
}
}
// Usage
const stateManager = new StateManager({
user: null,
recipes: [],
loading: false,
});
stateManager.subscribe((state) => {
console.log('State updated:', state);
// Update UI
});
stateManager.setState({ recipes: [...] });
Part 8: Utility Functions
Common Utilities
// Debounce
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Throttle
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Random ID
function generateId() {
return Math.random().toString(36).substr(2, 9);
}
// Slugify text
function slugify(text) {
return text
.toString()
.toLowerCase()
.trim()
.replace(/\s+/g, '-')
.replace(/[^\w\-]+/g, '');
}
// Format number with commas
function formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// Truncate text
function truncate(text, maxLength) {
return text.length > maxLength
? text.substring(0, maxLength - 3) + '...'
: text;
}
JavaScript Development Best Practices
Code Quality
- Use
constandletinstead ofvar - Use strict mode (
'use strict') - Add JSDoc comments for functions
- Use modern ES2024+ features
- Avoid global variables
- Use meaningful variable and function names
Asynchronous Code
- Prefer async/await over .then() chains
- Handle errors with try-catch
- Use Promise.all for parallel operations
- Implement retry logic for network requests
- Provide loading states for async operations
DOM & Events
- Use event delegation for dynamic content
- Clean up event listeners to prevent memory leaks
- Use dataset for custom data attributes
- Validate form input before submission
- Use debouncing/throttling for rapid events
Performance
- Minimize DOM manipulation
- Use document fragments for bulk inserts
- Implement lazy loading for images/lists
- Cache expensive computations
- Use requestAnimationFrame for animations
Security
- Sanitize user input to prevent XSS
- Use HTTPS for API calls
- Store tokens securely (HttpOnly cookies)
- Validate data from localStorage
- Implement CSRF protection
References & Resources
Official Documentation
- MDN Web Docs — Complete JavaScript reference
- ECMAScript 2024 Spec — Latest language specification
- Fetch API — HTTP requests with Fetch
- Web Storage API — LocalStorage and SessionStorage
Libraries & Tools
- Axios Documentation — Popular HTTP client library
- Vite Documentation — Build tool for modern web apps
- ESLint — Code linting for JavaScript
- Prettier — Code formatting tool
Learning Resources
- JavaScript.info — Modern JavaScript tutorial
- JavaScript 30 — 30-day JavaScript challenge
- You Don't Know JS — Deep dive into JavaScript
Related Skills
| Skill | Relationship |
|---|---|
| react-development | React component and hook patterns |
| vite-development | Build tooling for JS projects |
| nestjs | Server-side JS with NestJS framework |
| web-testing | Test JS apps with Playwright and DevTools |
Weekly Installs
6
Repository
practicalswan/a…t-skillsGitHub Stars
2
First Seen
Feb 26, 2026
Security Audits
Installed on
opencode6
gemini-cli6
claude-code6
github-copilot6
amp6
cline6