Skip to main content

JavaScript Functions

Functions are the building blocks of JavaScript. Understanding functions deeply is essential for writing clean, maintainable code.

Function Types

JavaScript offers multiple ways to define functions:

Function Declaration

function square(a) {
  return a * a;
}

Function Expression

const square = function (a) {
  return a * a;
};

Arrow Function

const square = (a) => {
  return a * a;
};

// Or with implicit return
const square = a => a * a;

Arrow Functions

Learn about arrow function syntax and behavior

More Topics

More function topics coming soon

Function Patterns

Higher-Order Functions

Functions that take other functions as arguments or return functions:
const map = (arr, fn) => arr.map(fn);
const double = x => x * 2;

map([1, 2, 3], double); // [2, 4, 6]

Currying

Transforming a function with multiple arguments into a sequence of functions:
const add = a => b => a + b;

const add5 = add(5);
add5(3); // 8
add5(10); // 15

Composition

Combining multiple functions into a single function:
const compose = (...fns) => x =>
  fns.reduceRight((acc, fn) => fn(acc), x);

const addOne = x => x + 1;
const double = x => x * 2;
const addOneThenDouble = compose(double, addOne);

addOneThenDouble(3); // 8 (3 + 1 = 4, 4 * 2 = 8)

Practical Examples

Memoization

Cache function results for better performance:
const memoize = fn => {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
};

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

fibonacci(10); // 55 (much faster with memoization)

Debounce

Delay function execution until after a period of inactivity:
const debounce = (fn, delay) => {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
};

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

// Only executes once, 300ms after the last call
handleSearch('a');
handleSearch('ab');
handleSearch('abc');

Throttle

Limit how often a function can be called:
const throttle = (fn, delay) => {
  let lastCall = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      fn(...args);
    }
  };
};

const handleScroll = throttle(() => {
  console.log('Scroll event handled');
}, 100);

window.addEventListener('scroll', handleScroll);

Best Practices

Each function should do one thing well. If a function is doing multiple things, consider splitting it into smaller functions.
Function names should clearly describe what they do. Use verbs for functions that perform actions.
Pure functions (no side effects, same input = same output) are easier to test and reason about.
Use early returns to reduce nesting and improve readability.

Common Pitfalls

Function Hoisting

Function declarations are hoisted, but function expressions are not:
// Works - function declaration is hoisted
sayHello(); // "Hello!"
function sayHello() {
  console.log('Hello!');
}

// Error - function expression is not hoisted
sayGoodbye(); // TypeError: sayGoodbye is not a function
const sayGoodbye = function() {
  console.log('Goodbye!');
};

this Context

Regular functions have dynamic this, arrow functions have lexical this:
const obj = {
  value: 42,
  regular: function() {
    console.log(this.value); // 42
  },
  arrow: () => {
    console.log(this.value); // undefined (lexical this)
  }
};

obj.regular(); // 42
obj.arrow(); // undefined

Build docs developers (and LLMs) love