JavaScript Functions

A function is a reusable block of code that you can run whenever you need it. Instead of writing the same logic in multiple places, you write it once in a function and call it by name.

Functions are one of the most important concepts in JavaScript. Almost everything you write will use them.

Function declarations

The most common way to define a function is a function declaration. Use the function keyword, give it a name, list any parameters in parentheses, and put the code in curly braces.

function greet(name) {
  console.log(`Hello, ${name}!`);
}

greet('Anna'); // Hello, Anna!
greet('Joe');  // Hello, Joe!

Return values

Use the return keyword to send a value back from the function. Once return runs, the function stops.

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

const result = add(3, 4);
console.log(result); // 7

Default parameters

You can give parameters a default value. If the caller does not pass an argument, the default is used.

function greet(name = 'stranger') {
  return `Hello, ${name}!`;
}

console.log(greet('Anna'));  // Hello, Anna!
console.log(greet());        // Hello, stranger!

Function expressions

A function expression assigns a function to a variable. The function itself can be anonymous (no name).

const greet = function(name) {
  return `Hello, ${name}!`;
};

console.log(greet('Anna')); // Hello, Anna!

The key difference from a function declaration: function expressions are not hoisted. You cannot call them before the line where they are defined.

// This works (declaration is hoisted)
sayHi();
function sayHi() { console.log('Hi'); }

// This throws a ReferenceError (expression is not hoisted)
sayHello();
const sayHello = function() { console.log('Hello'); };

Arrow functions

Arrow functions are a shorter syntax for function expressions. See the Arrow Functions page for details, including the important difference in how this works.

const add = (a, b) => a + b;
console.log(add(3, 4)); // 7

Rest parameters

The rest parameter syntax (...args) lets a function accept any number of arguments as an array.

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

console.log(sum(1, 2, 3));       // 6
console.log(sum(10, 20, 30, 40)); // 100

The rest parameter must be the last parameter in the list.

function logOrder(first, second, ...rest) {
  console.log(`First: ${first}`);
  console.log(`Second: ${second}`);
  console.log(`Others:`, rest);
}

logOrder('a', 'b', 'c', 'd');
// First: a
// Second: b
// Others: ['c', 'd']

Higher-order functions

A higher-order function is a function that either takes another function as an argument, or returns a function.

This sounds abstract, but you use them all the time with arrays. map(), filter(), and forEach() are all higher-order functions: you pass a function to them, and they call it for each item in the array.

const prices = [10, 25, 8, 40, 15];

// filter() takes a function and runs it on each item
const affordable = prices.filter(price => price < 20);
console.log(affordable); // [10, 8, 15]

You can also write your own:

function applyToAll(arr, fn) {
  return arr.map(fn);
}

const doubled = applyToAll([1, 2, 3], n => n * 2);
console.log(doubled); // [2, 4, 6]

Closures

A closure is when a function remembers the variables from the place where it was created, even after that outer function has finished running.

This is a powerful feature. Here is a practical example: a counter that keeps its own private state.

function makeCounter() {
  let count = 0; // this variable is private to the closure

  return function() {
    count++;
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

Each call to makeCounter() creates a separate count variable. The inner function closes over it, keeping it alive and private.

Common mistakes

Forgetting to return a value: A function without a return statement returns undefined. This is a common source of bugs.

function add(a, b) {
  a + b; // no return: this function returns undefined
}

console.log(add(3, 4)); // undefined

Calling a function vs referencing it: To run a function, you need the parentheses. Without them, you are just pointing at the function itself (useful when passing it as an argument).

function sayHi() {
  console.log('Hi');
}

sayHi();    // runs the function
sayHi;      // does nothing: this is just a reference to the function

setTimeout(sayHi, 1000);   // correct: pass the reference, let setTimeout call it
setTimeout(sayHi(), 1000); // wrong: this runs sayHi immediately and passes the result

See Arrow Functions for the shorter function syntax and how this behaves differently.