Functions are callable values with lexical scope, parameters, return contracts, receiver behavior, closure state, and synchronous, asynchronous, or generator execution forms.
Good function APIs expose ownership and failure clearly, inject effects for testing, clean up lazy resources, and optimize only after representative measurement.
A function in JavaScript is a reusable block of code that performs a specific task. Instead of repeating the same logic again and again, you define a function once and call it whenever you need it.
Functions make programs easier to read, test, debug, and maintain. They are also one of the most important parts of JavaScript because functions can be stored in variables, passed as arguments, returned from other functions, and used as callbacks.
function sayHello() {
console.log("Hello, JavaScript!");
}
sayHello(); // Hello, JavaScript!
A function usually has a name, parameters, a body, and optionally a return value. Parameters act like local variables that receive values when the function is called.
| Part | Meaning |
|---|---|
| function | Keyword used to define a function declaration |
| add | Function name |
| a, b | Parameters that receive input values |
| Function body | Code inside curly braces |
| return | Sends a value back to the caller |
function add(a, b) {
const total = a + b;
return total;
}
const result = add(10, 5);
console.log(result); // 15
A function declaration defines a named function with the function keyword. Function declarations are hoisted, which means you can call them before they appear in the source code.
console.log(greet("Asha")); // Hello, Asha!
function greet(name) {
return `Hello, ${name}!`;
}
A function expression creates a function and assigns it to a variable. Function expressions are not usable before the assignment line runs. They can be anonymous or named.
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(4, 5)); // 20
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
console.log(factorial(5)); // 120
Arrow functions provide compact function expressions and capture `this`, `arguments`, `super`, and `new.target` lexically. Use them for callbacks that should inherit surrounding context, not as constructors or object methods that need dynamic `this`.
Arrow functions do not create their own this, arguments, or constructor behavior. This makes them excellent for short callbacks, but regular functions are often better for object methods and constructors.
const square = number => number * number;
console.log(square(6)); // 36
const add = (a, b) => a + b;
console.log(add(10, 20)); // 30
const divide = (a, b) => {
if (b === 0) {
return "Cannot divide by zero";
}
return a / b;
};
console.log(divide(10, 2)); // 5
| Type | Hoisted? | Own this? | Best Use |
|---|---|---|---|
| Function declaration | Yes | Yes | Reusable named utilities |
| Function expression | No | Yes | Assigning functions to variables or properties |
| Arrow function | No | No | Short callbacks and lexical this |
Parameters are the names listed in the function definition. Arguments are the actual values passed when the function is called.
function introduce(name, role) {
return `${name} is a ${role}.`;
}
console.log(introduce("Maya", "developer"));
// name and role are parameters.
// "Maya" and "developer" are arguments.
Default parameters provide fallback values when an argument is missing or explicitly passed as undefined.
function createUser(name = "Guest", role = "Reader") {
return {
name,
role,
active: true
};
}
console.log(createUser());
console.log(createUser("Riya", "Admin"));
Rest parameters collect multiple arguments into an array. They are useful when a function can accept any number of values.
The return statement sends a value back to the code that called the function. It also stops function execution immediately. If a function does not return anything, it returns undefined.
function isEven(number) {
if (number % 2 !== 0) {
return false;
}
return true;
}
console.log(isEven(8)); // true
console.log(isEven(7)); // false
function logMessage(message) {
console.log(message);
}
console.log(logMessage("Hi")); // undefined
Variables declared inside a function are local to that function. Code outside the function cannot directly access those local variables.
function showCourse() {
const course = "JavaScript";
console.log(course);
}
showCourse(); // JavaScript
// console.log(course); // ReferenceError
A function can be defined inside another function. The inner function can access variables from the outer function because JavaScript uses lexical scope.
function outer() {
const message = "Hello from outer";
function inner() {
return message;
}
return inner();
}
console.log(outer()); // Hello from outer
A callback is a function passed as an argument to another function. The receiving function can call it later. Callbacks are common in events, timers, array methods, and asynchronous code.
function processUser(name, callback) {
const formattedName = name.trim().toUpperCase();
callback(formattedName);
}
processUser(" anika ", function(result) {
console.log(result); // ANIKA
});
A higher-order function is a function that takes another function as an argument, returns a function, or both. This idea powers many JavaScript patterns and built-in array methods.
function applyOperation(a, b, operation) {
return operation(a, b);
}
const add = (x, y) => x + y;
const multiply = (x, y) => x * y;
console.log(applyOperation(5, 3, add)); // 8
console.log(applyOperation(5, 3, multiply)); // 15
function createMultiplier(factor) {
return number => number * factor;
}
const double = createMultiplier(2);
console.log(double(10)); // 20
JavaScript array methods often receive functions as callbacks. Methods like map(), filter(), reduce(), find(), and forEach() are examples of higher-order functions.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(number => number * 2);
const evenNumbers = numbers.filter(number => number % 2 === 0);
const total = numbers.reduce((sum, number) => sum + number, 0);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(evenNumbers); // [2, 4]
console.log(total); // 15
A pure function returns the same output for the same input and does not change outside state. Pure functions are easier to test and reason about.
// Pure function
function calculateTax(price, rate) {
return price * rate;
}
// Impure function
let total = 0;
function addToTotal(amount) {
total += amount;
return total;
}
When a function is stored as an object property, it is called a method. Regular methods get this from how they are called. Arrow functions do not have their own this, so they are usually not ideal for object methods.
const user = {
name: "Tara",
greet() {
return `Hello, ${this.name}`;
}
};
console.log(user.greet()); // Hello, Tara
Before ES6 classes, constructor functions were commonly used to create objects. A constructor is called with new. Arrow functions cannot be used as constructors.
function User(name, email) {
this.name = name;
this.email = email;
}
User.prototype.login = function() {
return `${this.name} logged in`;
};
const user = new User("Kabir", "kabir@example.com");
console.log(user.login());
An IIFE is a function expression that runs immediately after it is created. IIFEs were often used to create private scope before ES modules became common.
(function() {
const message = "Private scope";
console.log(message);
})();
const result = (() => {
const a = 10;
const b = 20;
return a + b;
})();
console.log(result); // 30
Recursion happens when a function calls itself. A recursive function must have a base case, or it will keep calling itself until the call stack overflows.
function factorial(number) {
if (number <= 1) {
return 1;
}
return number * factorial(number - 1);
}
console.log(factorial(5)); // 120
A closure is created when a function remembers variables from the scope where it was created. Closures are useful for private state, function factories, timers, event handlers, and many real-world JavaScript patterns.
function createCounter() {
let count = 0;
return function increment() {
count += 1;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
An async function returns a promise. Inside an async function, the await keyword pauses execution until a promise settles. Async functions make asynchronous code easier to read.
async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// loadUser(1).then(user => console.log(user));
A generator function can pause and resume execution using yield. Generator functions use function* syntax and return an iterator.
The following example combines declarations, arrow functions, default parameters, rest parameters, callbacks, higher-order functions, and return values in a simple student report program.
function calculateAverage(...marks) {
const total = marks.reduce((sum, mark) => sum + mark, 0);
return total / marks.length;
}
const getGrade = average => {
if (average >= 90) return "A";
if (average >= 75) return "B";
if (average >= 60) return "C";
return "Needs Practice";
};
function createReport(name = "Student", marks = [], formatter) {
const average = calculateAverage(...marks);
const grade = getGrade(average);
const report = {
name,
marks,
average,
grade
};
return formatter ? formatter(report) : report;
}
const printReport = report =>
`${report.name}: ${report.average.toFixed(2)} average, Grade ${report.grade}`;
const result = createReport("Nisha", [88, 92, 79], printReport);
console.log(result);
Default parameters apply to omitted or undefined arguments, not null. Defaults are evaluated at call time from left to right in a parameter environment, so an earlier parameter can feed a later default. Keep dependent defaults obvious and normalize nullable input explicitly inside the body.
Rest parameters collect remaining arguments into a real array. Prefer them to `arguments` for new code because their position and meaning are explicit. Destructured parameters are convenient for options, but validate the whole input and provide a default source only when omission is valid.
Every path should honor one return contract. Falling off the end returns undefined, and an async function fulfills with undefined. Use discriminated result objects when callers must distinguish absence, validation failure, and operational failure; do not mix thrown errors, null, booleans, and data without a documented rule.
Keep functions small around one decision or transformation, but do not fragment a coherent algorithm into wrappers that only forward arguments. Names, parameter order, side effects, error behavior, and ownership matter more than line count.
A generator function returns an iterator and pauses at each `yield`. It is useful for lazy sequences, traversals, and state machines where the consumer controls progress. Early loop exit can call iterator return behavior, so place resource cleanup in `finally` when a generator owns a resource.
Async generators produce async iterators consumed with `for await...of`. They fit paginated or streaming values, but cancellation, backpressure, partial failure, and final cleanup remain part of the API contract. Do not buffer the full source merely to expose it through async syntax.
Test pure functions with boundaries and generated invariants; test effectful functions through injected clocks, storage, transport, randomness, and cancellation. Cover missing arguments, invalid types, thrown dependencies, asynchronous ordering, callback identity, and cleanup.
Measure hot functions only after correctness. Repeated allocation, hidden quadratic lookup, parsing, DOM work, and network calls usually matter more than call overhead. Preserve readable contracts and benchmark representative data in the target runtime.
Document accepted values, defaults, mutation, return variants, thrown errors, cancellation, concurrency, callback timing, and resource ownership. A function name alone cannot reveal whether work starts immediately, whether callbacks may repeat, or whether a returned collection shares nested objects.
Keep examples executable and include one failure path. Type declarations help tools, but runtime consumers and external data still need validation. Update documentation and tests together when a synchronous result becomes a Promise or a one-shot callback becomes a subscription.
A function is a reusable block of code that performs a task. It can receive input through parameters and send output back using <code>return</code>.
A function declaration is hoisted and can be called before it appears in code. A function expression is assigned to a variable and can be used only after that assignment runs.
Use arrow functions for short callbacks, array methods, promises, and cases where lexical <code>this</code> is helpful. Avoid them as constructors or object methods that need their own <code>this</code>.
Explore 500+ free tutorials across 20+ languages and frameworks.