How to Pass a Function as an Argument in JS Guide

Passing a function as an argument is the foundation of callbacks, event handlers, and array methods. Learn the syntax, the patterns, and how to think in functions-as-values.

6 min read

Passing a function as an argument means giving one function to another function so it can be called later. This is not a special syntax or keyword -- it is a natural consequence of functions being values in JavaScript. You pass a function the same way you pass a string or a number.

Here is the simplest example:

javascriptjavascript
function greet(name) {
  return `Hello, ${name}!`;
}
 
function printResult(fn, value) {
  const result = fn(value);
  console.log(result);
}
 
printResult(greet, "Alex"); // "Hello, Alex!"

Notice greet has no parentheses when passed. You are passing the function itself, not calling it. The printResult function receives that function and calls it itself, using whatever value it was given.

Passing a Reference vs Calling the Function

The most common mistake when passing functions around is calling the function when you actually mean to pass it uncalled, so the receiving function can call it at the right time instead:

javascriptjavascript
function getTime() {
  return new Date().toLocaleTimeString();
}
 
// Wrong: calls getTime immediately, passes the string result
setTimeout(getTime(), 1000);
 
// Correct: passes the function reference, setTimeout calls it later
setTimeout(getTime, 1000);

Think of it this way: getTime by itself refers to the function. Adding parentheses runs it immediately and gives you the result of running it right now. When you want another function to call yours later, pass the bare function name, not the result of calling it.

Passing a ReferenceCalling the Function
SyntaxsetTimeout(getTime, 1000)setTimeout(getTime(), 1000)
What is passedThe function itselfThe function's return value
When it runsLater, whenever the receiver calls itImmediately, before the receiver even runs

Inline Functions as Arguments

You do not need a named function. You can write the function directly where you pass it:

javascriptjavascript
const numbers = [1, 2, 3, 4];
 
// Inline function expression
const doubled = numbers.map(function(n) {
  return n * 2;
});
 
// Inline arrow function (shorter)
const tripled = numbers.map(n => n * 3);
 
console.log(doubled); // [2, 4, 6, 8]
console.log(tripled); // [3, 6, 9, 12]

Inline arrow functions are the most common pattern in modern JavaScript. They are short, readable, and keep the callback logic next to where it is used.

Built-in Methods That Take Functions

JavaScript's standard library is full of methods that accept function arguments:

MethodWhat the function receivesWhat it should return
array.map(fn)Each elementThe transformed element
array.filter(fn)Each elementtrue to keep, false to discard
array.reduce(fn, init)Accumulator, each elementThe new accumulator
array.forEach(fn)Each elementNothing (return is ignored)
array.find(fn)Each elementtrue when the match is found
array.sort(fn)Two elementsNegative, zero, or positive number
setTimeout(fn, ms)NothingNothing
addEventListener(event, fn)The event objectNothing

The convention is consistent: you provide the "what to do" function, and the method handles the "when and how."

Named Functions vs Inline Functions

Use a named function when the logic is reused or complex. Naming each step also makes a filter-then-map chain read almost like a sentence:

javascriptjavascript
function isExpensive(product) {
  return product.price > 50;
}
 
function formatProduct(product) {
  return `${product.name}: $${product.price}`;
}

Passing these named functions directly, without wrapping them in an extra arrow function, keeps the resulting chain short and easy to read from left to right:

javascriptjavascript
const products = [
  { name: "Book", price: 12 },
  { name: "Headphones", price: 79 },
  { name: "Pen", price: 3 },
];
 
const expensiveNames = products
  .filter(isExpensive)
  .map(formatProduct);
 
console.log(expensiveNames); // ["Headphones: $79"]

Named functions make the intent explicit. The filter step reads like a sentence: filter by isExpensive. Use inline arrows when the logic is a single expression that is clear without a name.

Named FunctionInline Function
ReusabilityCan be reused across multiple callsWritten fresh each time
Readability at call siteReads like a sentence, e.g. filter(isExpensive)Shows the full logic inline
Best forLogic that is reused or has a clear nameA single, simple expression used once

Practical Pattern: Custom Iteration

You can write your own function that accepts a callback, the same way the built-in array methods do:

javascriptjavascript
function forEachItem(items, action) {
  for (let i = 0; i < items.length; i++) {
    action(items[i], i);
  }
}

Calling it with an array and an inline arrow function runs that action once for every item, passing along the item and its index:

javascriptjavascript
const fruits = ["apple", "banana", "cherry"];
 
forEachItem(fruits, (fruit, index) => {
  console.log(`${index}: ${fruit}`);
});
 
// 0: apple
// 1: banana
// 2: cherry

Your function handles the loop. The caller's function decides what to do with each item. This separation is the core idea behind higher-order functions.

For more on this, see Higher Order Functions in JavaScript: Full Guide. For returning functions instead of taking them, see Returning Functions from Functions in JavaScript.

Practical Pattern: Conditional Execution

Passing functions lets you defer decisions:

javascriptjavascript
function when(condition, action) {
  if (condition) {
    action();
  }
}
 
const isLoggedIn = true;
 
when(isLoggedIn, () => {
  console.log("Welcome back!");
});
// "Welcome back!"

The when function does not know what "logged in" means. It only knows "if condition is true, run action." The caller provides both the condition value and the action.

Passing Methods: The this Problem

When you pass an object method as a callback, it loses its this binding, since only the function itself is passed along, not the object it was attached to:

javascriptjavascript
const user = {
  name: "Sara",
  greet() {
    console.log(`Hi, I'm ${this.name}`);
  },
};
 
// Loses this -- prints "Hi, I'm undefined"
setTimeout(user.greet, 1000);

There are two common fixes: wrap the call in an arrow function, or bind the method to the object ahead of time so this is locked in regardless of how it gets called later:

javascriptjavascript
// Fix 1: wrap in an arrow function
setTimeout(() => user.greet(), 1000);
 
// Fix 2: bind the method
setTimeout(user.greet.bind(user), 1000);

When you pass user.greet, you are passing the function without its object context. The arrow wrapper preserves the object reference. This is a common source of bugs with event handlers and timeouts.

Rune AI

Rune AI

Key Insights

  • Pass a function reference by writing its name without parentheses: someFunction.
  • Use arrow functions for inline callbacks: arr.map(n => n * 2).
  • Do not call the function when passing it -- that passes the return value, not the function.
  • Built-in functions like map, filter, forEach, and addEventListener all accept function arguments.
  • This pattern is the foundation of asynchronous JavaScript and functional programming.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between passing a function reference and calling it?

Passing a function reference means writing the name without parentheses: someFn. This gives the function to another function to call later. Writing someFn() calls it immediately and passes the return value instead.

Can I pass an anonymous function directly?

Yes. This is the most common pattern: numbers.map(function(n) { return n * 2; }) or with arrow syntax: numbers.map(n => n * 2).

Can I pass a method from an object as a callback?

Yes, but be careful with this. If the method uses this, it will lose its binding when passed as a standalone callback. Use .bind() or an arrow function wrapper to preserve the context.

Conclusion

Passing a function as an argument is not a special feature. It is a natural consequence of functions being values in JavaScript. Once you internalize that function references are no different from strings or numbers, callbacks, event handlers, and array methods all become variations of the same simple pattern.