All topics
General · Learning hub

JavaScript notes for developers

Master JavaScript with a curated set of 7 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore General notes
JavaScript

Async Programming (Promises & Async/Await)

JavaScript Async Programming Asynchronous programming is essential in JavaScript for handling operations like API calls, file I/O, and timers without blocking t

JavaScript Async Programming

Asynchronous programming is essential in JavaScript for handling operations like API calls, file I/O, and timers without blocking the main thread.

Promises

// Creating a promise
const promise = new Promise((resolve, reject) => {
  const success = true;
  
  if (success) {
    resolve('Operation successful');
  } else {
    reject('Operation failed');
  }
});

// Consuming promises
promise
  .then(result => {
    console.log(result);  // 'Operation successful'
    return 'Next value';
  })
  .then(result => {
    console.log(result);  // 'Next value'
  })
  .catch(error => {
    console.error(error);
  })
  .finally(() => {
    console.log('Always runs');
  });

// Async operation
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, name: 'John' });
      } else {
        reject('Invalid ID');
      }
    }, 1000);
  });
}

fetchUser(1)
  .then(user => console.log(user))
  .catch(error => console.error(error));

// Promise.all - wait for all
const promise1 = fetch('/api/users');
const promise2 = fetch('/api/posts');
const promise3 = fetch('/api/comments');

Promise.all([promise1, promise2, promise3])
  .then(([users, posts, comments]) => {
    console.log('All data loaded');
  })
  .catch(error => {
    console.error('One promise failed:', error);
  });

// Promise.allSettled - wait for all, don't fail
Promise.allSettled([promise1, promise2, promise3])
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Promise ${index} succeeded:`, result.value);
      } else {
        console.log(`Promise ${index} failed:`, result.reason);
      }
    });
  });

// Promise.race - first to complete
Promise.race([promise1, promise2, promise3])
  .then(result => {
    console.log('First to finish:', result);
  });

// Promise.any - first to succeed
Promise.any([promise1, promise2, promise3])
  .then(result => {
    console.log('First success:', result);
  })
  .catch(error => {
    console.log('All failed:', error);
  });

Async/Await

// Basic async/await
async function getUser(id) {
  const response = await fetch(`/api/users/${id}`);
  const user = await response.json();
  return user;
}

// Calling async function
getUser(1)
  .then(user => console.log(user))
  .catch(error => console.error(error));

// Or with async context
(async () => {
  try {
    const user = await getUser(1);
    console.log(user);
  } catch (error) {
    console.error(error);
  }
})();

// Error handling
async function fetchData() {
  try {
    const response = await fetch('/api/data');
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
    throw error;  // Re-throw or handle
  }
}

// Sequential vs Parallel
// Sequential (slower)
async function sequential() {
  const user = await fetchUser();      // Wait 1s
  const posts = await fetchPosts();    // Wait 1s
  const comments = await fetchComments();  // Wait 1s
  // Total: 3s
}

// Parallel (faster)
async function parallel() {
  const [user, posts, comments] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchComments()
  ]);
  // Total: 1s (all run simultaneously)
}

// Top-level await (in modules)
const data = await fetch('/api/data');
const json = await data.json();
export default json;

Callbacks vs Promises vs Async/Await

// Callback hell
getUser(1, (error, user) => {
  if (error) return console.error(error);
  
  getPosts(user.id, (error, posts) => {
    if (error) return console.error(error);
    
    getComments(posts[0].id, (error, comments) => {
      if (error) return console.error(error);
      
      console.log(comments);
    });
  });
});

// Promises - better
getUser(1)
  .then(user => getPosts(user.id))
  .then(posts => getComments(posts[0].id))
  .then(comments => console.log(comments))
  .catch(error => console.error(error));

// Async/await - best
async function loadData() {
  try {
    const user = await getUser(1);
    const posts = await getPosts(user.id);
    const comments = await getComments(posts[0].id);
    console.log(comments);
  } catch (error) {
    console.error(error);
  }
}
JavaScript

Modern JavaScript Patterns

Modern JavaScript Patterns Array Methods const numbers = [1, 2, 3, 4, 5]; // map - transform each element const doubled = numbers.map(n => n * 2); // [2, 4, 6,

Modern JavaScript Patterns

Array Methods

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

// map - transform each element
const doubled = numbers.map(n => n * 2);  // [2, 4, 6, 8, 10]

// filter - keep elements that pass test
const evens = numbers.filter(n => n % 2 === 0);  // [2, 4]

// reduce - accumulate values
const sum = numbers.reduce((acc, n) => acc + n, 0);  // 15

// find - first element that matches
const users = [
  { id: 1, name: 'John', active: true },
  { id: 2, name: 'Jane', active: false },
  { id: 3, name: 'Bob', active: true }
];

const firstActive = users.find(u => u.active);  // { id: 1, ... }

// findIndex - index of first match
const index = users.findIndex(u => u.name === 'Jane');  // 1

// some - check if any element passes test
const hasActive = users.some(u => u.active);  // true

// every - check if all elements pass test
const allActive = users.every(u => u.active);  // false

// forEach - iterate
users.forEach((user, index) => {
  console.log(`${index}: ${user.name}`);
});

// flatMap - map then flatten
const nested = [[1, 2], [3, 4], [5, 6]];
const flattened = nested.flatMap(arr => arr);  // [1,2,3,4,5,6]

// flat - flatten arrays
const deep = [1, [2, [3, [4, 5]]]];
console.log(deep.flat());     // [1, 2, [3, [4, 5]]]
console.log(deep.flat(2));    // [1, 2, 3, [4, 5]]
console.log(deep.flat(Infinity));  // [1, 2, 3, 4, 5]

Optional Chaining & Nullish Coalescing

// Optional chaining (?.) - ES2020
const user = {
  name: 'John',
  address: {
    city: 'NYC'
  }
};

// Old way
const city = user && user.address && user.address.city;

// ES2020 way
const city = user?.address?.city;  // 'NYC'
const country = user?.address?.country;  // undefined (no error)

// With arrays
const firstUser = users?.[0];

// With methods
user.greet?.();  // Only call if exists

// Nullish coalescing (??) - ES2020
const value1 = null ?? 'default';      // 'default'
const value2 = undefined ?? 'default'; // 'default'
const value3 = 0 ?? 'default';         // 0 (not null/undefined)
const value4 = '' ?? 'default';        // '' (not null/undefined)
const value5 = false ?? 'default';     // false (not null/undefined)

// Compare with ||
const v1 = 0 || 'default';      // 'default' (0 is falsy)
const v2 = 0 ?? 'default';      // 0 (0 is not null/undefined)

// Logical assignment operators
let obj = { a: 1 };

obj.a ||= 10;   // obj.a = obj.a || 10;
obj.b ??= 20;   // obj.b = obj.b ?? 20;
obj.c &&= 30;   // obj.c = obj.c && 30;

Map, Set, WeakMap, WeakSet

// Map - key-value pairs (any type as key)
const map = new Map();

map.set('name', 'John');
map.set(1, 'number key');
map.set({}, 'object key');

console.log(map.get('name'));  // 'John'
console.log(map.has('name'));  // true
console.log(map.size);         // 3

map.delete('name');
map.clear();

// Iterate
for (const [key, value] of map) {
  console.log(key, value);
}

// Set - unique values
const set = new Set();

set.add(1);
set.add(2);
set.add(2);  // Duplicate ignored

console.log(set.size);     // 2
console.log(set.has(1));   // true

set.delete(1);
set.clear();

// Convert array to set (remove duplicates)
const numbers = [1, 2, 2, 3, 3, 4];
const unique = [...new Set(numbers)];  // [1, 2, 3, 4]

// WeakMap - keys are objects, can be garbage collected
const weakMap = new WeakMap();
let obj = { id: 1 };

weakMap.set(obj, 'metadata');
console.log(weakMap.get(obj));  // 'metadata'

obj = null;  // Object can be garbage collected

// WeakSet - similar to Set but with object values only
const weakSet = new WeakSet();
const obj1 = { id: 1 };
const obj2 = { id: 2 };

weakSet.add(obj1);
weakSet.add(obj2);

console.log(weakSet.has(obj1));  // true
JavaScript

Interview Questions

JavaScript Interview Questions Comprehensive JavaScript interview questions covering fundamentals, ES6+, async programming, and advanced concepts. These questio

JavaScript Interview Questions

Comprehensive JavaScript interview questions covering fundamentals, ES6+, async programming, and advanced concepts. These questions are frequently asked in technical interviews.

Fundamentals

1. What is JavaScript?

JavaScript is a high-level, interpreted programming language. It's single-threaded, dynamically typed, and supports multiple programming paradigms (procedural, object-oriented, functional).

2. What are JavaScript data types?

Primitives: string, number, boolean, undefined, null, symbol, bigint. Objects: object, array, function, date, regexp.

3. What is the difference between == and ===?

== performs type coercion before comparison. === checks both value and type without coercion. Always prefer === for predictable behavior.

5 == '5'   // true (coercion)
5 === '5'  // false (different types)
0 == false  // true
0 === false // false
null == undefined   // true
null === undefined  // false

4. What is the difference between null and undefined?

undefined: variable declared but not assigned. null: intentional absence of value. typeof null is "object" (historical bug), typeof undefined is "undefined".

5. What are falsy values in JavaScript?

false, 0, -0, 0n (BigInt zero), "" (empty string), null, undefined, NaN. Everything else is truthy.

Scope & Closures

6. What is a closure?

A closure is a function that has access to variables in its outer scope, even after the outer function has returned. Closures are created every time a function is created.

function outer() {
  const count = 0;
  return function inner() {
    console.log(count);  // Accesses outer scope
  };
}

const fn = outer();
fn();  // 0

7. What is the difference between var, let, and const?

  • var: function-scoped, hoisted, can be redeclared

  • let: block-scoped, not hoisted to accessible state, cannot be redeclared

  • const: block-scoped, must be initialized, cannot be reassigned

8. What is hoisting?

Hoisting moves variable and function declarations to the top of their scope. var declarations are hoisted (initialized with undefined). Function declarations are fully hoisted. let/const are hoisted but not initialized (temporal dead zone).

9. What is the temporal dead zone?

The temporal dead zone is the period between entering scope and variable initialization. Accessing let/const variables before declaration causes ReferenceError.

this & Context

10. What is "this" in JavaScript?

"this" refers to the object executing the current function. Its value depends on how the function is called: global (window/global), object method (the object), constructor (new instance), event handler (element), arrow function (lexical this).

11. What is the difference between call(), apply(), and bind()?

call(): invokes function with specified this and arguments individually. apply(): invokes with this and arguments as array. bind(): creates new function with specified this, doesn't invoke immediately.

function greet(greeting, punctuation) {
  return `${greeting}, ${this.name}${punctuation}`;
}

const person = { name: 'John' };

greet.call(person, 'Hello', '!');     // 'Hello, John!'
greet.apply(person, ['Hello', '!']);  // 'Hello, John!'

const boundGreet = greet.bind(person, 'Hello');
boundGreet('!');  // 'Hello, John!'

12. How do arrow functions handle "this"?

Arrow functions don't have their own "this". They inherit "this" from the enclosing lexical scope (where they're defined, not where they're called).

Objects & Prototypes

13. What is prototypal inheritance?

Objects can inherit properties and methods from other objects through the prototype chain. Every object has a __proto__ property that points to its prototype.

14. What is the difference between __proto__ and prototype?

__proto__ is the actual object used in the prototype chain lookup. prototype is a property on constructor functions that becomes the __proto__ of instances created with new.

15. How do you create an object without a prototype?

const obj = Object.create(null);  // No prototype
console.log(obj.toString);  // undefined

Async Programming

16. What is a Promise?

A Promise represents the eventual completion (or failure) of an async operation. States: pending, fulfilled, rejected. Use .then() for success, .catch() for errors, .finally() for cleanup.

17. What is async/await?

async/await is syntactic sugar over Promises. async functions always return a Promise. await pauses execution until Promise resolves, making async code look synchronous.

18. What is the difference between Promise.all() and Promise.race()?

Promise.all(): waits for all promises, fails if any fails. Promise.race(): resolves/rejects with first settled promise. Promise.allSettled(): waits for all, never rejects. Promise.any(): first successful promise.

Event Loop

19. Explain the JavaScript event loop.

The event loop monitors the call stack and callback queue. When the call stack is empty, it moves callbacks from the queue to the stack for execution. Microtasks (Promises) have priority over macrotasks (setTimeout).

20. What is the difference between microtasks and macrotasks?

Microtasks (Promise callbacks, queueMicrotask) execute after current script, before rendering. Macrotasks (setTimeout, setInterval) execute after rendering. Microtasks have higher priority.

Functions

21. What is a higher-order function?

A function that takes another function as argument or returns a function. Examples: map, filter, reduce.

22. What is a pure function?

A function that: 1) Given same inputs, always returns same output. 2) Has no side effects (doesn't modify external state). Makes code predictable and testable.

23. What is function currying?

Currying transforms a function with multiple arguments into a sequence of functions each taking a single argument.

// Normal function
function add(a, b, c) {
  return a + b + c;
}

// Curried function
function addCurried(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

const result = addCurried(1)(2)(3);  // 6

ES6+ Features

24. What is destructuring?

Destructuring extracts values from arrays or properties from objects into distinct variables. Supports default values, renaming, and nested destructuring.

25. What is the spread operator?

Spread (...) expands iterables into individual elements. Used to copy/merge arrays, clone objects, pass array elements as function arguments.

26. What is the rest operator?

Rest (...) collects multiple elements into an array. Used in function parameters to accept variable number of arguments, or in destructuring to collect remaining elements.

27. What are template literals?

Template literals use backticks for strings. Features: string interpolation ${}, multiline strings, tagged templates.

Arrays & Objects

28. How do you check if a variable is an array?

Array.isArray([1, 2, 3]);  // true
Array.isArray('string');   // false

// Don't use:
typeof [] === 'object'     // true (not reliable)
[] instanceof Array        // true (can fail cross-frame)

29. What is the difference between shallow copy and deep copy?

Shallow copy: copies top-level properties, nested objects/arrays are still referenced. Deep copy: recursively copies all nested structures.

// Shallow copy
const original = { a: 1, b: { c: 2 } };
const shallow = { ...original };
shallow.b.c = 3;
console.log(original.b.c);  // 3 (shared reference)

// Deep copy
const deep = JSON.parse(JSON.stringify(original));
// Or use structuredClone()
const deep2 = structuredClone(original);

30. How do you remove duplicates from an array?

const numbers = [1, 2, 2, 3, 3, 4];
const unique = [...new Set(numbers)];  // [1, 2, 3, 4]

// Or
const unique = numbers.filter((val, index, arr) => arr.indexOf(val) === index);

Advanced Concepts

31. What is debouncing?

Debouncing delays function execution until after a specified time has passed since the last invocation. Useful for search inputs, window resize events.

function debounce(func, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

const search = debounce((query) => {
  console.log('Searching for:', query);
}, 300);

// User types 'hello' quickly
search('h');      // Cancelled
search('he');     // Cancelled
search('hel');    // Cancelled
search('hell');   // Cancelled
search('hello');  // Executes after 300ms

32. What is throttling?

Throttling limits function execution to once per specified time period. Useful for scroll events, mouse movements.

function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

const handleScroll = throttle(() => {
  console.log('Scrolled!');
}, 1000);

window.addEventListener('scroll', handleScroll);

33. What is memoization?

Memoization caches function results for specific inputs to avoid redundant calculations. Optimization technique for expensive operations.

function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

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

fibonacci(40);  // Fast with memoization

34. What is the module pattern?

Module pattern uses IIFE and closures to create private variables and expose public API. Provides encapsulation before ES6 modules.

const calculator = (function() {
  let result = 0;  // Private
  
  return {  // Public API
    add(n) {
      result += n;
      return this;
    },
    getResult() {
      return result;
    }
  };
})();

calculator.add(5).add(3);
console.log(calculator.getResult());  // 8

Practical Questions

35. How do you deep clone an object?

// Method 1: JSON (simple but has limitations)
const clone1 = JSON.parse(JSON.stringify(obj));
// Limitations: loses functions, undefined, symbols, dates become strings

// Method 2: structuredClone (modern, recommended)
const clone2 = structuredClone(obj);

// Method 3: Recursive deep clone
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof Array) return obj.map(item => deepClone(item));
  
  const cloned = {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key]);
    }
  }
  return cloned;
}

36. What is the difference between map() and forEach()?

map() creates and returns a new array with transformed values. forEach() just iterates, returns undefined. Use map() for transformation, forEach() for side effects.

37. How do you flatten a nested array?

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

// flat() method
nested.flat();           // [1, 2, [3, [4, 5]]]
nested.flat(2);          // [1, 2, 3, [4, 5]]
nested.flat(Infinity);   // [1, 2, 3, 4, 5]

// Recursive
function flatten(arr) {
  return arr.reduce((acc, val) => 
    Array.isArray(val) ? acc.concat(flatten(val)) : acc.concat(val),
    []
  );
}

38. How do you reverse a string?

const str = 'hello';
const reversed = str.split('').reverse().join('');  // 'olleh'

// Or with spread
const reversed2 = [...str].reverse().join('');

39. How do you check if a string is a palindrome?

function isPalindrome(str) {
  const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
  const reversed = cleaned.split('').reverse().join('');
  return cleaned === reversed;
}

isPalindrome('A man, a plan, a canal: Panama');  // true

40. How do you find the largest number in an array?

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

// Method 1: Math.max with spread
const max1 = Math.max(...numbers);  // 9

// Method 2: reduce
const max2 = numbers.reduce((max, n) => n > max ? n : max);

// Method 3: sort
const max3 = numbers.sort((a, b) => b - a)[0];

Common Patterns & Best Practices

41. What is event delegation?

Event delegation attaches a single event listener to a parent element instead of multiple listeners to children. Uses event bubbling to handle events from children.

// Instead of this:
const buttons = document.querySelectorAll('button');
buttons.forEach(btn => {
  btn.addEventListener('click', handleClick);
});

// Do this:
document.getElementById('container').addEventListener('click', (e) => {
  if (e.target.tagName === 'BUTTON') {
    handleClick(e);
  }
});

42. What is the difference between synchronous and asynchronous code?

Synchronous: executes line by line, blocks until complete. Asynchronous: non-blocking, continues execution, handles result via callbacks/promises when ready.

43. What is event bubbling and capturing?

Event bubbling: events propagate from target up through ancestors. Event capturing: events propagate from root down to target. Use addEventListener(event, handler, true) for capturing phase.

44. What is the difference between stopPropagation() and preventDefault()?

stopPropagation(): stops event from bubbling up. preventDefault(): prevents default browser behavior (e.g., form submission, link navigation).

45. What is strict mode?

"use strict" enables strict mode: prevents use of undeclared variables, makes this undefined in functions, disallows duplicate parameters, and catches silent errors.

JavaScript

Event Loop & Concurrency

JavaScript Event Loop & Concurrency JavaScript is single-threaded but can handle concurrent operations through the event loop, which manages the execution of co

JavaScript Event Loop & Concurrency

JavaScript is single-threaded but can handle concurrent operations through the event loop, which manages the execution of code, events, and callbacks.

Call Stack, Web APIs, and Task Queue

console.log('Start');

setTimeout(() => {
  console.log('Timeout');
}, 0);

Promise.resolve()
  .then(() => console.log('Promise 1'))
  .then(() => console.log('Promise 2'));

console.log('End');

// Output:
// Start
// End
// Promise 1
// Promise 2
// Timeout

// Why? Execution order:
// 1. Synchronous code runs first (Call Stack)
// 2. Microtasks (Promises) run next (Microtask Queue)
// 3. Macrotasks (setTimeout, setInterval) run last (Task Queue)

Microtasks vs Macrotasks

// Microtasks (higher priority):
// - Promise callbacks (.then, .catch, .finally)
// - queueMicrotask()
// - MutationObserver

// Macrotasks (lower priority):
// - setTimeout, setInterval
// - setImmediate (Node.js)
// - I/O operations
// - UI rendering

console.log('1');

setTimeout(() => console.log('2 - setTimeout'), 0);

Promise.resolve().then(() => console.log('3 - Promise'));

queueMicrotask(() => console.log('4 - queueMicrotask'));

console.log('5');

// Output:
// 1
// 5
// 3 - Promise
// 4 - queueMicrotask
// 2 - setTimeout

Web Workers (True Concurrency)

// Main thread
const worker = new Worker('worker.js');

// Send message to worker
worker.postMessage({ type: 'CALCULATE', numbers: [1, 2, 3, 4, 5] });

// Receive message from worker
worker.onmessage = (event) => {
  console.log('Result from worker:', event.data);
};

worker.onerror = (error) => {
  console.error('Worker error:', error);
};

// worker.js
self.onmessage = (event) => {
  const { type, numbers } = event.data;
  
  if (type === 'CALCULATE') {
    // Heavy computation in separate thread
    const result = numbers.reduce((sum, n) => sum + n, 0);
    self.postMessage(result);
  }
};
JavaScript

ES6+ Features & Syntax

Modern JavaScript ES6+ Features ES6 (ES2015) and later versions introduced significant improvements to JavaScript. Understanding these features is essential for

Modern JavaScript ES6+ Features

ES6 (ES2015) and later versions introduced significant improvements to JavaScript. Understanding these features is essential for modern JavaScript development.

Let, Const, and Block Scope

// var - function scoped, hoisted
var x = 1;
if (true) {
  var x = 2;  // Same variable
  console.log(x);  // 2
}
console.log(x);  // 2

// let - block scoped, not hoisted
let y = 1;
if (true) {
  let y = 2;  // Different variable
  console.log(y);  // 2
}
console.log(y);  // 1

// const - block scoped, cannot be reassigned
const z = 1;
// z = 2;  // Error!

// const with objects/arrays - reference is constant, not content
const user = { name: 'John' };
user.name = 'Jane';  // ✅ OK - modifying property
user.age = 30;       // ✅ OK - adding property
// user = {};        // ❌ Error - cannot reassign

const numbers = [1, 2, 3];
numbers.push(4);     // ✅ OK - modifying array
// numbers = [];     // ❌ Error - cannot reassign

Arrow Functions

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

// Arrow function
const add = (a, b) => a + b;

// Single parameter - parentheses optional
const square = x => x * x;

// No parameters - parentheses required
const greet = () => console.log('Hello');

// Multiple statements - need braces and return
const calculate = (a, b) => {
  const sum = a + b;
  const product = a * b;
  return { sum, product };
};

// Lexical 'this' binding
function Person() {
  this.age = 0;
  
  // Traditional function - 'this' refers to global object
  setInterval(function() {
    this.age++;  // Won't work as expected
  }, 1000);
  
  // Arrow function - 'this' refers to Person
  setInterval(() => {
    this.age++;  // ✅ Works correctly
  }, 1000);
}

// Array methods with arrows
const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);
const hasThree = numbers.some(n => n === 3);
const allPositive = numbers.every(n => n > 0);

Destructuring

// Array destructuring
const [first, second, third] = [1, 2, 3];
console.log(first);  // 1

// Skip elements
const [a, , c] = [1, 2, 3];
console.log(a, c);  // 1, 3

// Rest operator
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head);  // 1
console.log(tail);  // [2, 3, 4, 5]

// Default values
const [x = 0, y = 0] = [10];
console.log(x, y);  // 10, 0

// Object destructuring
const user = { name: 'John', age: 30, email: 'john@example.com' };
const { name, age } = user;
console.log(name, age);  // 'John', 30

// Rename variables
const { name: userName, age: userAge } = user;
console.log(userName);  // 'John'

// Default values
const { name, age, country = 'USA' } = user;
console.log(country);  // 'USA'

// Nested destructuring
const data = {
  user: {
    name: 'John',
    address: {
      city: 'NYC',
      zip: '10001'
    }
  }
};

const { user: { name, address: { city } } } = data;
console.log(name, city);  // 'John', 'NYC'

// Function parameter destructuring
function greet({ name, age }) {
  console.log(`Hello ${name}, you are ${age}`);
}

greet(user);  // Hello John, you are 30

Spread & Rest Operators

// Spread operator - expands iterables

// Arrays
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];  // [1,2,3,4,5,6]

// Clone array
const original = [1, 2, 3];
const copy = [...original];

// Add to array
const withExtra = [...arr1, 4, 5];

// Objects
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 };  // { a: 1, b: 2, c: 3, d: 4 }

// Clone object
const person = { name: 'John', age: 30 };
const clone = { ...person };

// Override properties
const updated = { ...person, age: 31 };

// Rest operator - collects arguments
function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}

console.log(sum(1, 2, 3, 4));  // 10

// With other parameters
function multiply(multiplier, ...numbers) {
  return numbers.map(n => n * multiplier);
}

console.log(multiply(2, 1, 2, 3));  // [2, 4, 6]

Template Literals

// String interpolation
const name = 'John';
const age = 30;
const message = `Hello, ${name}! You are ${age} years old.`;

// Multiline strings
const html = `
  <div>
    <h1>${name}</h1>
    <p>Age: ${age}</p>
  </div>
`;

// Expressions in templates
const result = `Sum: ${2 + 3}`;
const price = 19.99;
const tax = `Total: $${(price * 1.1).toFixed(2)}`;

// Tagged templates
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    return result + str + (values[i] ? `<mark>${values[i]}</mark>` : '');
  }, '');
}

const highlighted = highlight`Hello ${name}, you are ${age} years old`;
// 'Hello <mark>John</mark>, you are <mark>30</mark> years old'

Enhanced Object Literals

// Property shorthand
const name = 'John';
const age = 30;

// Old way
const person1 = { name: name, age: age };

// ES6 way
const person2 = { name, age };

// Method shorthand
const obj = {
  // Old way
  greet: function() {
    console.log('Hello');
  },
  
  // ES6 way
  greet() {
    console.log('Hello');
  },
  
  // Async method
  async fetchData() {
    const data = await fetch('/api/data');
    return data.json();
  }
};

// Computed property names
const propName = 'score';
const game = {
  [propName]: 100,
  [`${propName}Max`]: 1000,
  ['player' + 1]: 'John'
};
console.log(game);  // { score: 100, scoreMax: 1000, player1: 'John' }

Classes

// ES6 Classes
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
  
  // Static method
  static create(name, age) {
    return new Person(name, age);
  }
  
  // Getter
  get info() {
    return `${this.name} (${this.age})`;
  }
  
  // Setter
  set info(value) {
    const [name, age] = value.split(' ');
    this.name = name;
    this.age = parseInt(age);
  }
}

const john = new Person('John', 30);
john.greet();  // Hello, I'm John
console.log(john.info);  // John (30)

// Inheritance
class Employee extends Person {
  constructor(name, age, jobTitle) {
    super(name, age);  // Call parent constructor
    this.jobTitle = jobTitle;
  }
  
  greet() {
    super.greet();  // Call parent method
    console.log(`I work as a ${this.jobTitle}`);
  }
}

const emp = new Employee('Jane', 28, 'Developer');
emp.greet();
// Hello, I'm Jane
// I work as a Developer

Modules (Import/Export)

// utils.js - Named exports
export const PI = 3.14159;
export function add(a, b) {
  return a + b;
}
export class Calculator {
  // ...
}

// Or export all at once
const PI = 3.14159;
function add(a, b) {
  return a + b;
}
export { PI, add };

// Default export (one per file)
export default function multiply(a, b) {
  return a * b;
}

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

// Importing
import multiply from './utils.js';  // Default import
import { PI, add } from './utils.js';  // Named imports
import multiply, { PI, add } from './utils.js';  // Both
import * as utils from './utils.js';  // All as namespace

// Rename imports
import { add as sum } from './utils.js';

// Re-exporting
export { add } from './utils.js';
export * from './utils.js';

Default Parameters & Rest/Spread

// Default parameters
function greet(name = 'Guest', greeting = 'Hello') {
  return `${greeting}, ${name}!`;
}

console.log(greet());  // 'Hello, Guest!'
console.log(greet('John'));  // 'Hello, John!'
console.log(greet('John', 'Hi'));  // 'Hi, John!'

// Computed default values
function createUser(name, id = Date.now()) {
  return { name, id };
}

// Rest parameters
function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}

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

// Spread in function calls
const nums = [1, 2, 3];
console.log(Math.max(...nums));  // 3
JavaScript

Closures, Scopes & Hoisting

JavaScript Closures, Scopes & Hoisting Understanding scope, closures, and hoisting is fundamental to mastering JavaScript. These concepts are frequently tested

JavaScript Closures, Scopes & Hoisting

Understanding scope, closures, and hoisting is fundamental to mastering JavaScript. These concepts are frequently tested in interviews.

Closures

A closure is a function that has access to variables in its outer (enclosing) lexical scope, even after the outer function has returned.

// Basic closure
function outer() {
  const message = 'Hello';
  
  function inner() {
    console.log(message);  // Accesses outer scope
  }
  
  return inner;
}

const fn = outer();
fn();  // 'Hello' - even though outer() has returned

// Counter with closure
function createCounter() {
  let count = 0;  // Private variable
  
  return {
    increment() {
      return ++count;
    },
    decrement() {
      return --count;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment());  // 1
console.log(counter.increment());  // 2
console.log(counter.getCount());   // 2
// console.log(counter.count);     // undefined - private!

// Module pattern with closure
const calculator = (function() {
  let result = 0;  // Private state
  
  return {
    add(n) {
      result += n;
      return this;
    },
    subtract(n) {
      result -= n;
      return this;
    },
    getResult() {
      return result;
    },
    reset() {
      result = 0;
      return this;
    }
  };
})();

calculator.add(5).add(3).subtract(2);  // Chaining
console.log(calculator.getResult());   // 6

// Event handlers with closures
function createClickHandlers() {
  const handlers = [];
  
  for (let i = 0; i < 3; i++) {
    handlers.push(function() {
      console.log(`Button ${i} clicked`);
    });
  }
  
  return handlers;
}

const handlers = createClickHandlers();
handlers[0]();  // 'Button 0 clicked'
handlers[1]();  // 'Button 1 clicked'
handlers[2]();  // 'Button 2 clicked'

Scope Types

// Global scope
var globalVar = 'I am global';

// Function scope
function myFunction() {
  var functionVar = 'I am function-scoped';
  console.log(globalVar);      // ✅ Can access global
  console.log(functionVar);    // ✅ Can access function
}

// console.log(functionVar);   // ❌ Error - not accessible outside

// Block scope (let & const)
if (true) {
  var varVariable = 'var is function scoped';
  let letVariable = 'let is block scoped';
  const constVariable = 'const is block scoped';
}

console.log(varVariable);    // ✅ Accessible
// console.log(letVariable);  // ❌ Error
// console.log(constVariable);// ❌ Error

// Lexical scope
function outer() {
  const outerVar = 'outer';
  
  function middle() {
    const middleVar = 'middle';
    
    function inner() {
      const innerVar = 'inner';
      console.log(innerVar);   // ✅ Own scope
      console.log(middleVar);  // ✅ Parent scope
      console.log(outerVar);   // ✅ Grandparent scope
    }
    
    inner();
  }
  
  middle();
}

Hoisting

// Variable hoisting with var
console.log(x);  // undefined (not error!)
var x = 5;

// Equivalent to:
// var x;
// console.log(x);
// x = 5;

// let and const are NOT hoisted to accessible state
// console.log(y);  // ❌ ReferenceError: Cannot access before initialization
let y = 5;

// Function hoisting
greet();  // ✅ Works! Function declarations are hoisted

function greet() {
  console.log('Hello');
}

// Function expressions are NOT hoisted
// sayHi();  // ❌ Error
const sayHi = function() {
  console.log('Hi');
};

// Class hoisting - NOT hoisted
// const p = new Person();  // ❌ Error
class Person {
  constructor(name) {
    this.name = name;
  }
}

this Keyword

// Global context
console.log(this);  // Window (browser) or global (Node.js)

// Object method
const person = {
  name: 'John',
  greet() {
    console.log(this.name);  // 'this' refers to person
  }
};

person.greet();  // 'John'

// Losing 'this' context
const greetFn = person.greet;
greetFn();  // undefined - 'this' is now global

// Solutions:
// 1. Arrow function (lexical 'this')
const person2 = {
  name: 'Jane',
  greet: function() {
    setTimeout(() => {
      console.log(this.name);  // 'this' refers to person2
    }, 100);
  }
};

// 2. bind()
const boundGreet = person.greet.bind(person);
boundGreet();  // 'John'

// 3. call() and apply()
const otherPerson = { name: 'Bob' };
person.greet.call(otherPerson);  // 'Bob'
person.greet.apply(otherPerson); // 'Bob'

// Constructor function
function Car(make, model) {
  this.make = make;
  this.model = model;
}

const car = new Car('Toyota', 'Camry');
console.log(car.make);  // 'Toyota'
JavaScript

Prototypes & Inheritance

JavaScript Prototypes & Inheritance JavaScript uses prototypal inheritance. Every object has a prototype object from which it inherits properties and methods. P

JavaScript Prototypes & Inheritance

JavaScript uses prototypal inheritance. Every object has a prototype object from which it inherits properties and methods.

Prototype Chain

// Constructor function
function Person(name, age) {
  this.name = name;
  this.age = age;
}

// Add method to prototype
Person.prototype.greet = function() {
  console.log(`Hello, I'm ${this.name}`);
};

Person.prototype.getAge = function() {
  return this.age;
};

const john = new Person('John', 30);
john.greet();  // 'Hello, I'm John'

// Prototype chain lookup
console.log(john.name);           // Own property
console.log(john.greet);          // From Person.prototype
console.log(john.toString);       // From Object.prototype

// Check prototype
console.log(john.__proto__ === Person.prototype);  // true
console.log(Person.prototype.__proto__ === Object.prototype);  // true
console.log(Object.prototype.__proto__);  // null (end of chain)

// hasOwnProperty
console.log(john.hasOwnProperty('name'));   // true
console.log(john.hasOwnProperty('greet'));  // false (in prototype)

// instanceof
console.log(john instanceof Person);  // true
console.log(john instanceof Object);  // true

Prototypal Inheritance

// Parent constructor
function Animal(name) {
  this.name = name;
}

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating`);
};

// Child constructor
function Dog(name, breed) {
  Animal.call(this, name);  // Call parent constructor
  this.breed = breed;
}

// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// Add child methods
Dog.prototype.bark = function() {
  console.log(`${this.name} says woof!`);
};

const dog = new Dog('Buddy', 'Golden Retriever');
dog.eat();   // 'Buddy is eating' (inherited)
dog.bark();  // 'Buddy says woof!' (own)

console.log(dog instanceof Dog);     // true
console.log(dog instanceof Animal);  // true

// ES6 Classes (syntactic sugar over prototypes)
class AnimalES6 {
  constructor(name) {
    this.name = name;
  }
  
  eat() {
    console.log(`${this.name} is eating`);
  }
}

class DogES6 extends AnimalES6 {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  
  bark() {
    console.log(`${this.name} says woof!`);
  }
}

Object.create() and Object.setPrototypeOf()

// Object.create() - creates object with specific prototype
const personPrototype = {
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

const john = Object.create(personPrototype);
john.name = 'John';
john.greet();  // 'Hello, I'm John'

// Object.create with properties
const jane = Object.create(personPrototype, {
  name: {
    value: 'Jane',
    writable: true,
    enumerable: true
  },
  age: {
    value: 28,
    writable: true
  }
});

// Object.setPrototypeOf() - change prototype (avoid in production)
const dog = { name: 'Buddy' };
Object.setPrototypeOf(dog, personPrototype);
dog.greet();  // 'Hello, I'm Buddy'

// Object.getPrototypeOf()
console.log(Object.getPrototypeOf(john) === personPrototype);  // true

Keep your JavaScript knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever