To detect balanced parentheses, use a stack to track opening brackets. For each character, push opening brackets onto the stack and pop for closing brackets, checking if they match. If the stack is empty at the end and all brackets match, the string is balanced. Example:
function isBalanced(str) {
const stack = [];
const pairs = { '(': ')', '{': '}', '[': ']' };
for (const char of str) {
if (pairs[char]) stack.push(char);
else if (Object.values(pairs).includes(char)) {
if (pairs[stack.pop()] !== char) return false;
}
}
return stack.length === 0;
}
console.log(isBalanced('({[]})')); // true
console.log(isBalanced('([)]')); // false
JavaScriptLRU CacheData Structures
An LRU cache stores key-value pairs with a fixed capacity, evicting the least recently used item when full. Implement it using a Map for O(1) access and to maintain insertion order, or a combination of an object and a doubly linked list for explicit control. Update the order on get/set operations. Example:
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return -1;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key, value) {
if (this.cache.has(key)) this.cache.delete(key);
else if (this.cache.size >= this.capacity) this.cache.delete(this.cache.keys().next().value);
this.cache.set(key, value);
}
}
const cache = new LRUCache(2);
cache.set(1, 'one');
cache.set(2, 'two');
console.log(cache.get(1)); // 'one'
cache.set(3, 'three');
console.log(cache.get(2)); // -1
JavaScriptCustom PromiseImplementation
A promise-like class (MyPromise) manages asynchronous operations with pending, fulfilled, or rejected states. It supports .then() for chaining by returning a new promise that resolves with the result of the callback. Use a queue to store .then() callbacks and handle asynchronous resolution. Example:
A deep clone function recursively copies an object, including all nested objects and arrays, while handling circular references, special objects (e.g., Date, RegExp), and primitive values. Use a WeakMap to track already cloned objects to avoid infinite recursion with circular references. Iterate through object properties or array elements, cloning each recursively. Example:
function deepClone(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (seen.has(obj)) return seen.get(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
const clone = Array.isArray(obj) ? [] : {};
seen.set(obj, clone);
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clone[key] = deepClone(obj[key], seen);
}
}
return clone;
}
const obj = { a: 1, b: { c: 2 } };
const cloned = deepClone(obj);
obj.b.c = 3;
console.log(cloned.b.c); // 2
Function composition applies functions in sequence, where the output of one function is the input to the next. Implement a compose function that takes multiple functions and returns a new function that applies them from right to left (i.e., compose(f, g, h)(x) = f(g(h(x)))). Use reduce to chain the functions. Example:
function compose(...fns) {
return x => fns.reduceRight((result, fn) => fn(result), x);
}
const add1 = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const composed = compose(square, double, add1);
console.log(composed(2)); // 16 (square(double(add1(2))) = square(4) = 16)
JavaScriptDebounceThrottleUtility Functions
Debounce delays a function’s execution until a specified time after the last call. Throttle limits execution to once per interval. Both use setTimeout to control timing and clearTimeout to reset. Debounce is useful for input events; throttle for scroll or resize events. Example:
function debounce(fn, delay) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn(...args), delay);
};
}
function throttle(fn, interval) {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= interval) {
lastCall = now;
fn(...args);
}
};
}
const debouncedLog = debounce(console.log, 1000);
debouncedLog('Test'); // Logs after 1s
const throttledLog = throttle(console.log, 1000);
throttledLog('Test'); throttledLog('Test'); // Logs once per second
JavaScriptArray FlatteningImplementation
Flatten a nested array by recursively processing each element. If an element is an array, flatten it; otherwise, include it in the result. Use reduce for a functional approach or recursion for clarity. Example:
Memoization caches a function’s results for given inputs to avoid redundant computation. Use a Map to store argument-result pairs, serializing arguments to a string key for complex inputs. Handle edge cases like non-primitive arguments. Example:
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 slowAdd = (a, b) => {
console.log('Computing...');
return a + b;
};
const memoizedAdd = memoize(slowAdd);
console.log(memoizedAdd(1, 2)); // Computing..., 3
console.log(memoizedAdd(1, 2)); // 3 (cached)
A retry mechanism retries failed API requests with increasing delays (exponential backoff) to avoid overwhelming the server. Implement it with async/await, calculating delays as base * 2^attempt. Limit retries and handle errors gracefully. Example:
Enforce functional programming (FP) by promoting pure functions, immutability, and composition. Use ESLint rules (e.g., fp/no-mutation) to prevent side effects and mutations. Encourage libraries like Ramda or Lodash FP for functional utilities. Define immutable data structures with const and Object.freeze. Use higher-order functions and function composition for reusable logic. Conduct code reviews to ensure adherence to FP principles like referential transparency. Provide FP training and document patterns in a style guide. Use TypeScript to enforce function signatures and immutability. Monitor FP adoption with static analysis tools. Example:
const add = x => y => x + y;
const double = x => x * 2;
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const addThenDouble = compose(double, add(5));
console.log(addThenDouble(3)); // 16
JavaScriptPolyfillbind Method
A polyfill for Function.prototype.bind creates a new function with a fixed this context and optional pre-set arguments. Use apply to call the original function with the bound this and concatenated arguments. Example:
Write reusable utility functions by ensuring they are pure, single-responsibility, and composable. Use descriptive names and TypeScript for type safety. Design functions to accept generic inputs and return predictable outputs. Group related utilities in modules (e.g., arrayUtils, stringUtils) and publish as a shared npm package. Use higher-order functions for flexibility. Document with JSDoc and provide unit tests. Encourage composition over inheritance, using functions like pipe or compose. Review utilities in team discussions to ensure broad applicability. Example:
Pure functions always return the same output for the same input, have no side effects, and depend only on their arguments. Impure functions may produce side effects (e.g., modifying external state) or depend on external state. In projects, use pure functions for business logic (e.g., calculations, data transformations) to ensure testability and predictability. Use impure functions for I/O operations (e.g., API calls, DOM updates). Structure code to isolate impure functions in services or hooks, keeping core logic pure. Example:
// Pure
const sum = (a, b) => a + b;
// Impure
let counter = 0;
const increment = () => counter++;
// Project example
const calculateTax = (amount, rate) => amount * rate; // Pure
const fetchData = async () => await fetch('/api/data').then(res => res.json()); // Impure
console.log(calculateTax(100, 0.1)); // 10
JavaScriptCode ReadabilityMaintainability
Ensure readability and maintainability with consistent coding standards enforced via ESLint and Prettier. Use TypeScript for type safety and JSDoc for documentation. Implement a style guide with naming conventions and patterns. Conduct thorough code reviews with pull request templates. Use a monorepo to share utilities and configurations. Automate linting, formatting, and testing in CI/CD pipelines. Organize code into modular, single-responsibility components. Provide training on clean code principles and pair programming. Monitor technical debt with tools like SonarQube and address it in refactoring sprints. Example:
// .eslintrc.js
module.exports = {
rules: { 'max-lines-per-function': ['error', 50], 'no-unused-vars': 'error' },
extends: ['plugin:jsdoc/recommended']
};
// example.ts
/** Calculates total price with tax */
function calculateTotal(price: number, taxRate: number): number {
return price * (1 + taxRate);
}
JavaScriptHigher-Order FunctionsBusiness Logic
Higher-order functions (HOFs) take or return functions, enabling reusable and composable business logic. In projects, use HOFs like map, filter, and reduce for data transformations (e.g., processing orders). Create custom HOFs for domain-specific tasks, such as logging or validation wrappers. For example, wrap API calls with retry logic or memoization. Use composition to chain HOFs for complex workflows. Ensure HOFs are pure and documented for clarity. Example:
Follow naming principles: use descriptive, intention-revealing names (e.g., calculateTotal over calc). Use camelCase for functions and variables, PascalCase for classes. Prefix booleans with is/has/should (e.g., isValid). Use nouns for variables (e.g., userList) and verbs for functions (e.g., fetchData). Avoid abbreviations unless widely understood (e.g., id). Ensure consistency across the codebase, enforced via ESLint rules. Reflect domain terminology in names to align with business logic. Review names during code reviews to ensure clarity. Example:
Avoid side effects in utility modules by writing pure functions that rely only on inputs and produce predictable outputs. Use const for immutable data and Object.freeze for objects. Avoid global state or DOM manipulation. Use dependency injection to provide external dependencies. Test for purity with Jest, ensuring no external state changes. Enforce with ESLint rules (e.g., no-global-assign). Document side-effect-free guarantees in JSDoc. Isolate impure logic in separate services. Example:
export const sumArray = arr => arr.reduce((sum, num) => sum + num, 0);
// Impure alternative to avoid
export let total = 0;
export const addToTotal = num => total += num;
console.log(sumArray([1, 2, 3])); // 6, no side effects
JavaScriptDesign PatternsProduction Code
Implemented patterns like Module, Singleton, Factory, and Observer in production. The Module pattern encapsulates private state (e.g., API client configuration). Singleton ensures a single instance of a service (e.g., logger). Factory creates objects dynamically (e.g., UI components). Observer (via EventEmitter) handles event-driven logic (e.g., user notifications). Example:
Design a state management utility with a publish/subscribe pattern, immutable state, and TypeScript for type safety. Use a single source of truth with a reducer-like function to update state. Provide subscribe/unsubscribe methods for components to react to changes. Support middleware for logging or async actions. Ensure extensibility with plugins for custom reducers or effects. Test with Jest to verify state updates and subscriptions. Example:
Apply SOLID principles: Single Responsibility (each function/class handles one task, e.g., separate data fetching and rendering). Open/Closed (extend behavior via composition, not modification). Liskov Substitution (use interfaces for interchangeable components). Interface Segregation (define small, specific interfaces). Dependency Inversion (inject dependencies, e.g., via constructor). Use TypeScript to enforce contracts and modular architecture to isolate concerns. Example:
Refactor large switch-case statements using the strategy pattern or object literals to map cases to functions. This improves readability, extensibility, and testability. Define a lookup table or strategy interface to handle each case, reducing conditional logic. Example:
The strategy pattern defines a family of interchangeable algorithms, encapsulating each one and making them swappable. Implement it with a context class that delegates to a strategy interface, allowing runtime switching. Useful for payment processing or sorting algorithms. Example:
interface PaymentStrategy { pay(amount: number): string; }
class CreditCard implements PaymentStrategy {
pay(amount: number) { return `Paid ${amount} via Credit Card`; }
}
class PayPal implements PaymentStrategy {
pay(amount: number) { return `Paid ${amount} via PayPal`; }
}
class PaymentProcessor {
constructor(private strategy: PaymentStrategy) {}
process(amount: number) { return this.strategy.pay(amount); }
}
const processor = new PaymentProcessor(new CreditCard());
console.log(processor.process(100)); // Paid 100 via Credit Card
JavaScriptDependency InjectionDesign Patterns
Dependency injection (DI) provides dependencies to a class or function, improving testability and flexibility. In JavaScript, use constructor injection or function parameters. For example, inject a logger or API service into a component. Use libraries like InversifyJS for complex DI. In a React app, pass services via props or context. DI isolates dependencies, making mocking easier in tests. Example:
Enforce standards with a shared ESLint and Prettier configuration in a monorepo, published as an npm package. Use TypeScript for type safety. Integrate linting, formatting, and tests in CI/CD pipelines with GitHub Actions, rejecting non-compliant code. Use Husky for pre-commit hooks to run checks. Define a style guide with examples and ADRs. Conduct code reviews with pull request templates, requiring senior approval. Provide editor integrations (e.g., VS Code settings). Monitor adherence with SonarQube and train teams on standards. Example:
// .eslintrc.js
module.exports = { extends: ['shared-config'], rules: { 'no-console': 'error' } };
// .github/workflows/ci.yml
name: CI
on: [push]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run lint
- run: npm run format:check
JavaScriptTestingBest Practices
Best practices for JavaScript test cases include: Write clear, descriptive test names (e.g., 'should fetch user data'). Use AAA pattern (Arrange, Act, Assert). Mock external dependencies to isolate tests. Test edge cases, errors, and happy paths. Ensure tests are independent and repeatable. Use code coverage tools (e.g., Istanbul) to identify gaps. Run tests in CI/CD pipelines. Use Jest snapshots sparingly and review them. Write integration tests for critical flows. Document test intent in comments or a test plan. Example:
Write unit tests for an async function by mocking the external API with Jest or MSW (Mock Service Worker). Use async/await in tests to handle promises. Mock fetch or axios to return controlled responses. Test success, error, and edge cases (e.g., network failure). Ensure mocks are reset between tests to avoid state leakage. Example:
Handle circular dependencies by refactoring to remove them, as they can cause runtime errors or bundler issues. Use dependency injection to defer dependencies. Reorganize modules to follow a layered architecture (e.g., utils depend on nothing). Use dynamic imports for lazy loading. Detect circular dependencies with tools like Madge or ESLint. If unavoidable, resolve at runtime with proxies or lazy initialization. Document fixes in ADRs. Example:
// Before: Circular dependency
// a.js
import { b } from './b'; export const a = () => b();
// b.js
import { a } from './a'; export const b = () => console.log('b');
// After: Refactored
// utils.js
export const log = () => console.log('b');
// a.js
import { log } from './utils'; export const a = () => log();
// b.js
import { log } from './utils'; export const b = () => log();
a(); // b
JavaScriptCode SmellsRefactoring
Identify code smells (e.g., long functions, duplicated code, tight coupling) with static analysis tools like SonarQube or ESLint. Conduct code reviews and pair programming to spot issues. Profile with Chrome DevTools to find performance smells. Eliminate smells by refactoring: split large functions, extract reusable utilities, use design patterns (e.g., strategy for conditionals), and introduce dependency injection. Write tests to ensure refactoring preserves behavior. Document changes in ADRs and prioritize smells based on impact. Example:
// Before: Long function
function process(data) {
if (data.type === 'a') console.log(data.value * 2);
else console.log(data.value + 1);
}
// After: Refactored
const strategies = {
a: value => value * 2,
b: value => value + 1
};
function process(data) {
console.log(strategies[data.type](data.value));
}
process({ type: 'a', value: 5 }); // 10
JavaScriptThird-Party LibrariesSafety
Write a wrapper around a third-party library to handle errors, validate inputs, and provide fallbacks. Use try/catch for synchronous calls and promise rejection handling for async calls. Log errors to an observability tool. Validate inputs with a schema (e.g., Zod). Provide fallback behavior for missing or broken APIs. Use TypeScript to enforce library contracts. Test with mocks to simulate library failures. Example:
Debug production issues with observability tools like Sentry for error tracking and OpenTelemetry for tracing. Use Chrome DevTools’ Performance tab for profiling CPU and memory usage. Log structured data with correlation IDs to trace user flows. Implement feature toggles to isolate problematic code. Use source maps to map minified code to source. Profile with Performance API for custom metrics. Reproduce issues in a staging environment with similar data. Automate debugging with Puppeteer scripts in CI/CD to simulate flows. Document findings in a post-mortem for team learning. Example:
function trackPerformance() {
const start = performance.now();
// Complex logic
const data = Array(10000).fill().map((_, i) => i * 2);
console.log(`Took ${performance.now() - start}ms`);
}
trackPerformance();
// Sentry integration
import * as Sentry from '@sentry/browser';
Sentry.init({ dsn: 'https://[email protected]/123' });
try { trackPerformance(); } catch (err) { Sentry.captureException(err); }
JavaScriptFallbacksGraceful Degradation
Implement fallbacks and graceful degradation by using feature detection to check for API support (e.g., navigator.serviceWorker). Provide alternative logic for unsupported features (e.g., polling instead of WebSocket). Cache data in localStorage for offline scenarios. Serve simplified UI for legacy browsers using progressive enhancement. Monitor fallback usage with telemetry to assess impact. Test fallbacks with browser emulators like BrowserStack. Document fallback strategies in a style guide. Example:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(() => console.log('Registered'));
} else {
console.log('Service Worker not supported, using fallback');
setInterval(() => fetch('/api/data').then(res => res.json()), 5000);
}
JavaScriptSingle ResponsibilitySeparation of Concerns
Enforce single responsibility by splitting large functions into smaller, focused ones, each handling one task. Use composition to combine functions. Separate concerns by isolating data fetching, business logic, and rendering. Use modules to encapsulate related logic. Enforce with ESLint rules (e.g., max-lines-per-function). Review code to ensure clarity and modularity. Test each function independently to verify responsibility. Example:
// Before: Large function
function handleUser() {
const user = fetch('/api/user').then(res => res.json());
console.log(user.name);
document.getElementById('app').innerHTML = user.name;
}
// After: Separated concerns
const fetchUser = () => fetch('/api/user').then(res => res.json());
const logUser = user => console.log(user.name);
const renderUser = user => document.getElementById('app').innerHTML = user.name;
async function handleUser() {
const user = await fetchUser();
logUser(user);
renderUser(user);
}
JavaScriptPublish/SubscribeEventEmitter
Implement a publish/subscribe system with an EventEmitter class that stores event listeners in a map and supports on, off, and emit methods. Allow multiple listeners per event and pass data to callbacks during emission. Ensure thread-safety by copying listeners during emit to avoid mutation issues. Example:
class EventEmitter {
constructor() { this.events = new Map(); }
on(event, callback) {
if (!this.events.has(event)) this.events.set(event, []);
this.events.get(event).push(callback);
}
off(event, callback) {
const callbacks = this.events.get(event);
if (callbacks) this.events.set(event, callbacks.filter(cb => cb !== callback));
}
emit(event, ...args) {
const callbacks = this.events.get(event)?.slice() || [];
callbacks.forEach(cb => cb(...args));
}
}
const emitter = new EventEmitter();
emitter.on('greet', name => console.log(`Hello, ${name}`));
emitter.emit('greet', 'Alice'); // Hello, Alice
JavaScriptCyclomatic ComplexityCode Quality
Measure cyclomatic complexity with tools like ESLint (complexity rule) or SonarQube, which count decision points (e.g., if, switch). Improve by refactoring complex functions: extract logic into smaller functions, use strategy patterns for conditionals, and simplify nested loops. Write tests to ensure refactoring preserves behavior. Set complexity thresholds in CI/CD to reject high-complexity code. Review complex code in team discussions to find simpler alternatives. Example:
// Before: High complexity
function process(data) {
if (data.type === 'a') {
if (data.value > 0) return data.value * 2;
else return 0;
} else if (data.type === 'b') return data.value + 1;
}
// After: Lower complexity
const processors = {
a: value => value > 0 ? value * 2 : 0,
b: value => value + 1
};
function process(data) {
return processors[data.type]?.(data.value) ?? 0;
}
console.log(process({ type: 'a', value: 5 })); // 10
JavaScriptRace ConditionsConcurrency
Detect race conditions by logging async operations with timestamps and correlation IDs, using tools like Sentry or OpenTelemetry. Reproduce in tests with simulated delays. Handle race conditions with mutex-like locks (e.g., Promise-based locks), debouncing async calls, or versioning state updates. Use abort controllers to cancel outdated requests. Monitor production with telemetry to catch unexpected state changes. Document race condition fixes in ADRs and test with Jest timers. Example: