JavaScript Functions Explained from Basic to Advanced Concepts
Functions are the backbone of JavaScript. This article walks through every function concept from basic declarations to closures, callbacks, pure functions, and higher-order functions.
A function in JavaScript starts as a simple idea: a named block of code you can call from anywhere. But functions in JavaScript are also first-class objects. That means you can assign them to variables, pass them as arguments, return them from other functions, and give them properties.
This article maps the full landscape, from the first function hello() you write to closures and pure functions. Each concept links to a dedicated article where you can go deeper.
The Function Landscape
Every function concept in this article builds on the same starting point: a function you can define and call. From there, the concepts branch into three layers that build on each other.
- Syntax layer: declarations, expressions, and arrow functions, plus parameters and return values.
- Scope layer: lexical scope and closures, which explain what a function can see and remember.
- Pattern layer: callbacks, higher-order functions, pure functions, and recursion, which combine the first two layers into reusable techniques.
Each section below covers one layer at a time, starting with syntax.
Defining Functions: Three Syntaxes
All three syntaxes create a callable function. The differences are in hoisting, this binding, and verbosity.
Function Declaration
function greet(name) {
return `Hello, ${name}!`;
}Hoisted, meaning JavaScript moves the whole definition to the top of its scope. This lets you call it before the line where it appears in the file. It works well for standalone utility functions used throughout a file.
Function Expression
const greet = function(name) {
return `Hello, ${name}!`;
};Not hoisted, since the function only exists once this line actually runs. This form is useful for assigning functions conditionally or passing them inline as arguments.
Arrow Function
const greet = (name) => `Hello, ${name}!`;Shorter syntax, and no own this, which makes arrow functions a natural fit for callbacks and array methods like map and filter.
For a side-by-side comparison of all three styles, see the dedicated arrow functions guide.
Parameters and Arguments
Parameters are the named placeholders in the function definition. Arguments are the actual values passed during the call.
// price, quantity, taxRate are parameters
function calcTotal(price, quantity, taxRate = 0.08) {
return price * quantity * (1 + taxRate);
}
// 20, 3 are arguments. taxRate uses the default 0.08
calcTotal(20, 3); // 64.8Default parameters let you set fallback values. When an argument is missing or undefined, the default kicks in.
Rest parameters collect extra arguments into an array:
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10Return Values
Every function returns something. If there is a return statement, that value is sent back. If not, the function returns undefined.
function double(n) {
return n * 2;
}
function logIt(msg) {
console.log(msg);
// No return -- implicitly returns undefined
}
const a = double(5); // a is 10
const b = logIt("hi"); // prints "hi", b is undefinedScope: Where Variables Live
A function creates its own scope. Variables declared inside a function are not visible outside it:
function outer() {
const secret = "hidden";
console.log(secret); // "hidden"
}
outer();
console.log(secret); // ReferenceError: secret is not definedThe reverse is not true, however: a function nested inside another function can see and use the outer function's variables, because the inner function is written inside the outer one's scope.
function outer() {
const message = "hello";
function inner() {
console.log(message); // Can access outer's message
}
inner();
}
outer(); // prints "hello"This is lexical scope: a function's visibility is determined by where it is written, not where it is called. For a deeper look, read JavaScript Lexical Scope: A Complete Tutorial.
Closures: Functions That Remember
A closure is created when a function accesses variables from an outer scope, and that inner function survives beyond the outer function's execution.
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3createCounter runs once and returns the inner function. Even though createCounter has finished, the inner function still remembers the count variable. That is a closure.
Closures are how JavaScript achieves data privacy and stateful functions without classes. Learn them thoroughly in JavaScript Closures Deep Dive: Complete Guide.
Callbacks: Functions as Arguments
A callback is a function passed into another function to be called later:
function fetchData(callback) {
setTimeout(() => {
callback("Data loaded");
}, 1000);
}
fetchData((message) => {
console.log(message); // "Data loaded" (after 1 second)
});Callbacks are the traditional way to handle asynchronous operations in JavaScript, but they show up constantly in synchronous code too. Array methods like forEach accept a callback and run it once for every item in the array:
[1, 2, 3].forEach((n) => console.log(n * 2));For a complete explanation, see the dedicated callback tutorial.
Higher-Order Functions
A higher-order function is a function that takes another function as an argument or returns a function. The array methods map, filter, and reduce are built-in higher-order functions:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(evens); // [2, 4]
console.log(sum); // 15You can also write your own. The createCounter example from the closures section is a higher-order function, since it returns a function.
Pure Functions
A pure function always returns the same output for the same input and has no side effects:
// Pure: same inputs always produce the same output
function add(a, b) {
return a + b;
}
// Impure: depends on external state
let taxRate = 0.08;
function addTax(price) {
return price * (1 + taxRate); // taxRate can change
}Pure functions are predictable, testable, and composable. They are a cornerstone of functional programming. Learn more in Pure vs Impure Functions in JavaScript Explained.
Recursion
A recursive function calls itself:
function countdown(n) {
if (n <= 0) return;
console.log(n);
countdown(n - 1);
}
countdown(3);
// 3
// 2
// 1Recursion is useful for tree traversal, mathematical sequences, and any problem that naturally breaks into smaller identical subproblems. See How to Use Recursion in JavaScript: Full Tutorial.
Learning Path
If you are new to functions, follow this order:
-
Declarations and calling -- learn to define and invoke a function.
-
Parameters and return values -- pass data in, get data out.
-
Scope -- understand where variables are visible.
-
Closures -- functions that remember their outer variables.
-
Callbacks and higher-order functions -- functions as values.
-
Pure functions -- predictable, side-effect-free code.
Each concept builds on the one before it. You do not need to understand closures to write a useful function. Start simple and add concepts as you need them.
Rune AI
Key Insights
- Functions are reusable blocks of code that can be declared, expressed, or written as arrows.
- Parameters receive input. The return keyword sends output back.
- Functions create their own scope. Inner functions can access outer variables, forming closures.
- Callbacks and higher-order functions let you pass functions as values.
- Pure functions always produce the same output for the same input and have no side effects.
Frequently Asked Questions
What is the most important function concept to learn first?
Are arrow functions always better than regular functions?
What is the hardest function concept for beginners?
Conclusion
JavaScript functions start simple -- a reusable block of code with a name. But they scale into a rich system: expressions, arrows, parameters, return values, scope, closures, callbacks, higher-order functions, and pure functions. You do not need to master everything at once. Build from declarations to calling, from calling to scope, from scope to closures, and from closures to functional patterns.
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.