JavaScript Cheatsheet

Functions

Use this JavaScript reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Function Declaration vs Expression vs Arrow

// Function declaration — hoisted, has its own `this`, `arguments`
function add(a, b) { return a + b; }

// Function expression — not hoisted
const add = function(a, b) { return a + b; };

// Named function expression — name available inside itself only
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};

// Arrow function (ES6) — NOT hoisted, no own `this`, no `arguments`, no `new`
const add = (a, b) => a + b;         // implicit return
const square = x => x * x;           // single param, no parens needed
const greet = () => "hello";         // no params
const getObj = () => ({ key: 1 });   // return object literal — wrap in parens

// Arrow with block body
const add = (a, b) => {
  const sum = a + b;
  return sum;
};

Parameters

Defaults (ES6)

function greet(name = "World") {
  return `Hello, ${name}!`;
}
greet()         // "Hello, World!"
greet("Alice")  // "Hello, Alice!"

// Defaults can reference previous params or call functions
function createId(prefix = "id", n = Date.now()) {
  return `${prefix}_${n}`;
}

// undefined triggers default; null does NOT
greet(undefined) // "Hello, World!"
greet(null)      // "Hello, null!"

Rest Parameters (ES6)

function sum(...nums) {
  return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4) // 10

// Rest must be last
function log(level, ...messages) {
  console.log(`[${level}]`, ...messages);
}

// vs the legacy `arguments` object (not in arrow functions)
function legacy() {
  console.log(arguments); // array-like object, not a real array
  const arr = Array.from(arguments);
}

Destructuring Parameters

// Object destructuring with defaults
function display({ name = "Unknown", age = 0 } = {}) {
  return `${name}, ${age}`;
}
display({ name: "Alice", age: 30 });
display(); // uses default empty object

// Array destructuring
function first([a, b]) {
  return a;
}

Return Values

function nothing() {}           // returns undefined
function explicit() { return; } // returns undefined
function value() { return 42; } // returns 42

// Multiple return values via destructuring
function minMax(arr) {
  return [Math.min(...arr), Math.max(...arr)];
}
const [min, max] = minMax([3, 1, 4, 1, 5]);

// Return early (guard clause)
function divide(a, b) {
  if (b === 0) return null;
  return a / b;
}

Closures

// A closure captures variables from its outer scope
function makeCounter(start = 0) {
  let count = start;
  return {
    increment() { return ++count; },
    decrement() { return --count; },
    value()     { return count; },
  };
}
const counter = makeCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.value();     // 2

// Classic closure pitfall with var in loops
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // prints 3 3 3 (all same i)
}
// Fix with let (block scope) or IIFE
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // 0 1 2
}

// IIFE pattern (Immediately Invoked Function Expression)
(function () {
  let private = 42;
})();
// private is not accessible here

const result = (() => 42)();

Higher-Order Functions

// Functions as arguments
function apply(fn, value) { return fn(value); }
apply(Math.sqrt, 16); // 4

// Functions as return values
function multiplier(factor) {
  return x => x * factor;
}
const double = multiplier(2);
const triple = multiplier(3);
double(5); // 10
triple(5); // 15

// Common higher-order patterns
[1, 2, 3].map(x => x * 2);      // [2, 4, 6]
[1, 2, 3].filter(x => x > 1);   // [2, 3]
[1, 2, 3].reduce((a, b) => a + b, 0); // 6

this Binding

// Regular function — `this` depends on call site
function show() { console.log(this); }
show();              // global (or undefined in strict mode)
obj.show();          // obj
show.call(ctx);      // ctx
show.apply(ctx, []); // ctx
const bound = show.bind(ctx);
bound();             // ctx

// Arrow function — `this` is lexically inherited from enclosing scope
const obj = {
  name: "Alice",
  greet: function() {
    const inner = () => console.log(this.name); // this = obj
    inner();
  },
};

// Method shorthand (not an arrow — has own this)
const obj2 = {
  name: "Bob",
  greet() { console.log(this.name); }, // this = obj2
};

call, apply, bind

function greet(greeting, punct) {
  return `${greeting}, ${this.name}${punct}`;
}
const user = { name: "Alice" };

greet.call(user, "Hello", "!");    // "Hello, Alice!"
greet.apply(user, ["Hello", "!"]); // "Hello, Alice!" (args as array)
const bound = greet.bind(user, "Hi"); // returns new function
bound(".");                        // "Hi, Alice."

// Partial application with bind
function multiply(a, b) { return a * b; }
const double = multiply.bind(null, 2);
double(5); // 10

Generator Functions (ES6)

function* range(start, end, step = 1) {
  for (let i = start; i < end; i += step) {
    yield i;
  }
}

for (const n of range(0, 5)) {
  console.log(n); // 0 1 2 3 4
}

[...range(0, 5)]       // [0, 1, 2, 3, 4]
Array.from(range(0, 5)) // [0, 1, 2, 3, 4]

// Manual iteration
const gen = range(0, 3);
gen.next(); // { value: 0, done: false }
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: undefined, done: true }

// yield* delegates to another iterable
function* concat(...iterables) {
  for (const it of iterables) yield* it;
}

// Two-way communication via next(value)
function* adder() {
  let total = 0;
  while (true) {
    const n = yield total;
    total += n ?? 0;
  }
}

Async Functions (ES2017)

async function fetchUser(id) {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error("Not found");
  return res.json();
}

// Arrow async
const fetchUser = async (id) => {
  const data = await fetch(`/api/users/${id}`).then(r => r.json());
  return data;
};

// Always returns a Promise
fetchUser(1).then(console.log).catch(console.error);

(See the Async cheatsheet for full coverage.)

Function Composition and Currying

// Composition
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
const pipe    = (...fns) => x => fns.reduce((v, f) => f(v), x);

const double = x => x * 2;
const addOne = x => x + 1;
const transform = pipe(double, addOne);
transform(3); // 7

// Currying
const curry = fn => {
  const arity = fn.length;
  return function curried(...args) {
    if (args.length >= arity) return fn(...args);
    return (...more) => curried(...args, ...more);
  };
};

const add = curry((a, b, c) => a + b + c);
add(1)(2)(3); // 6
add(1, 2)(3); // 6
add(1)(2, 3); // 6

// Manual curry
const add = a => b => a + b;
const add5 = add(5);
add5(3); // 8

Memoization

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 fib = memoize(function(n) {
  if (n <= 1) return n;
  return fib(n - 1) + fib(n - 2);
});

Useful Patterns

// IIFE
const result = (function() { return 42; })();
const result2 = (() => 42)();

// Function with options object
function request(url, { method = "GET", headers = {}, body } = {}) {
  return fetch(url, { method, headers, body });
}

// Throttle
function throttle(fn, ms) {
  let last = 0;
  return function(...args) {
    const now = Date.now();
    if (now - last >= ms) {
      last = now;
      return fn.apply(this, args);
    }
  };
}

// Debounce
function debounce(fn, ms) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), ms);
  };
}

// Once
function once(fn) {
  let called = false, result;
  return function(...args) {
    if (!called) { called = true; result = fn.apply(this, args); }
    return result;
  };
}