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.
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:
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:
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 Reference | Calling the Function | |
|---|---|---|
| Syntax | setTimeout(getTime, 1000) | setTimeout(getTime(), 1000) |
| What is passed | The function itself | The function's return value |
| When it runs | Later, whenever the receiver calls it | Immediately, 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:
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:
| Method | What the function receives | What it should return |
|---|---|---|
array.map(fn) | Each element | The transformed element |
array.filter(fn) | Each element | true to keep, false to discard |
array.reduce(fn, init) | Accumulator, each element | The new accumulator |
array.forEach(fn) | Each element | Nothing (return is ignored) |
array.find(fn) | Each element | true when the match is found |
array.sort(fn) | Two elements | Negative, zero, or positive number |
setTimeout(fn, ms) | Nothing | Nothing |
addEventListener(event, fn) | The event object | Nothing |
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:
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:
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 Function | Inline Function | |
|---|---|---|
| Reusability | Can be reused across multiple calls | Written fresh each time |
| Readability at call site | Reads like a sentence, e.g. filter(isExpensive) | Shows the full logic inline |
| Best for | Logic that is reused or has a clear name | A 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:
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:
const fruits = ["apple", "banana", "cherry"];
forEachItem(fruits, (fruit, index) => {
console.log(`${index}: ${fruit}`);
});
// 0: apple
// 1: banana
// 2: cherryYour 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:
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:
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:
// 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
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.
Frequently Asked Questions
What is the difference between passing a function reference and calling it?
Can I pass an anonymous function directly?
Can I pass a method from an object as a callback?
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.
More in this topic
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.
How to Loop Through Arrays Using JS for Loops Guide
Looping through arrays is the most practical use of JavaScript for loops. Learn how to access each element by index, avoid off-by-one errors, and choose between for, for...of, and forEach.