JavaScript tutorials  /  Functions in JavaScript
Chapter 8 · JavaScript

Functions in JavaScript

A function in JavaScript is a reusable block of code designed to perform a particular task. It is executed when it is invoked (called) by some event or by another part of the code. Functions can accept inputs called parameters and can return a value as output.

Think of a function like a small machine you build once and use many times. You give it some input, it does some work, and it gives you back a result. Instead of writing the same code again and again, you write it once inside a function and just call the function whenever you need it.

Imagine an online food delivery app like Zomato or Swiggy. Every time a user places an order, the system needs to calculate the total bill, including item price, taxes, delivery charges, and discounts. Instead of writing this calculation logic every time a new order comes in, developers write a single function called calculateTotalBill(items, taxRate, deliveryFee, discount). Whenever an order is placed, this function is simply called with the relevant order details, and it returns the final amount. This makes the codebase easier to maintain, test, and update — if the tax calculation logic changes, developers only need to update it in one place.

Functions are essential because they promote code reusability, reduce redundancy, and make programs easier to read, test, and debug. Without functions, developers would have to duplicate the same logic across multiple places in their code, making it hard to maintain and prone to errors. Functions also help in breaking down complex problems into smaller, manageable pieces, support modular programming, and enable abstraction by hiding implementation details behind a simple function call.

  • Function Declaration: A named function defined using the function keyword at the top level of a script or inside a block. Function declarations are hoisted, meaning they can be called before they appear in the code.
  • Function Expression: A function assigned to a variable. It can be named or anonymous. Unlike declarations, function expressions are not hoisted with their definition, so they must be defined before use.
  • Arrow Function: A concise syntax for writing functions introduced in ES6, using the => syntax. Arrow functions do not have their own 'this' binding and are commonly used for short, single-purpose functions and callbacks.
  • Anonymous Function: A function without a name, often used as an argument to other functions such as event handlers or array methods like map, filter, and forEach.
  • Immediately Invoked Function Expression (IIFE): A function that is defined and executed immediately after its creation. It is commonly used to create a private scope and avoid polluting the global namespace.
  • Callback Function: A function passed as an argument to another function, to be executed later, often used in asynchronous operations like API calls, timers, and event listeners.
  • Generator Function: A special type of function defined using function* syntax that can pause and resume its execution using the yield keyword, useful for handling iterators and lazy evaluation.
// Function Declaration function functionName(parameter1, parameter2) { // function body return result; } // Function Expression const functionName = function(parameter1, parameter2) { return result; }; // Arrow Function const functionName = (parameter1, parameter2) => { return result; }; // Calling a function functionName(argument1, argument2);
Suppose you are building an e-commerce checkout page where you need to calculate the final price of a cart multiple times — whenever an item is added, removed, or a coupon is applied. Writing the price calculation logic (subtotal + tax - discount) every single time this happens would lead to repeated, hard-to-maintain code. How do you avoid this repetition and ensure the calculation logic stays consistent and easy to update everywhere it's used?
Basic Function Declaration
A simple function that adds two numbers and returns the result.
JavaScript
function addNumbers(a, b) { return a + b; } console.log(addNumbers(5, 3)); console.log(addNumbers(10, 20));
8 30
The addNumbers function takes two parameters, a and b, adds them together using the return statement, and sends the result back to wherever the function was called. Each call to addNumbers(5, 3) and addNumbers(10, 20) reuses the same logic with different inputs.
Function Expression with Default Parameters
A function expression that calculates a shopping cart total, using a default parameter for tax rate.
JavaScript
const calculateTotal = function(price, quantity, taxRate = 0.05) { const subtotal = price * quantity; const tax = subtotal * taxRate; return subtotal + tax; }; console.log(calculateTotal(100, 2)); console.log(calculateTotal(100, 2, 0.1));
210 220
calculateTotal is stored in a variable as a function expression. It has a default parameter taxRate set to 0.05, which is used only if no third argument is passed. The first call uses the default 5% tax, while the second call overrides it with 10%.
Arrow Function for Array Transformation
Using an arrow function with the map method to convert an array of prices by applying a discount.
JavaScript
const prices = [100, 250, 500]; const applyDiscount = (price) => price - price * 0.1; const discountedPrices = prices.map(applyDiscount); console.log(discountedPrices);
[ 90, 225, 450 ]
applyDiscount is an arrow function that takes a single price and returns the discounted value. It is passed as a callback to the map method, which applies it to every element of the prices array and returns a new array of discounted prices.
Callback Function with setTimeout
Demonstrating how a callback function is executed after a delay, simulating an asynchronous API response.
JavaScript
function fetchUserData(callback) { console.log('Fetching user data...'); setTimeout(() => { const userData = { name: 'Aditi', age: 28 }; callback(userData); }, 1000); } fetchUserData(function(data) { console.log('User data received:', data); });
Fetching user data... User data received: { name: 'Aditi', age: 28 }
fetchUserData accepts a callback function as its parameter. It first logs a message, then simulates a delayed operation using setTimeout. After one second, it calls the callback function with the fetched userData, printing the result to the console.
  • Forgetting to Use the return Statement: A common mistake is writing a function that performs a calculation but forgets to return the result, causing the function to return undefined. For example, writing 'function add(a, b) { a + b; }' without a return statement means calling add(2, 3) will output undefined instead of 5, leading to confusing bugs, especially when the result is used elsewhere in the code.
  • Confusing Arrow Functions and 'this' Binding: Developers often use arrow functions as object methods, expecting 'this' to refer to the object. However, arrow functions do not bind their own 'this'; they inherit it from the surrounding lexical scope. This leads to bugs such as 'this.value' being undefined inside an arrow function method, especially in class-based or object-oriented code.
  • Not Understanding Function Hoisting Differences: Function declarations are hoisted completely (including their definition), but function expressions and arrow functions assigned to variables declared with let or const are not. Calling a function expression before its definition results in a ReferenceError due to the temporal dead zone, which confuses developers coming from languages without such hoisting rules.
  • Overusing Global Variables Inside Functions: Modifying global variables directly inside functions instead of using parameters and return values creates tightly coupled, hard-to-test code. This often leads to unpredictable behavior when multiple functions modify the same global state, making debugging large applications extremely difficult.
  • Passing Too Many Parameters: Defining functions with a large number of positional parameters, such as createUser(name, age, email, address, phone, role), makes function calls error-prone since it's easy to mix up the order of arguments. This also makes the function signature hard to read and maintain as the application grows.
  • Keep Functions Small and Focused: Each function should ideally do one thing and do it well, following the Single Responsibility Principle. Small, focused functions are easier to test, debug, and reuse across different parts of an application, such as separating validateInput() and saveToDatabase() into distinct functions instead of combining them into one large function.
  • Use Descriptive Function Names: Name functions based on what they do, using verb-based names like calculateTax(), fetchUserProfile(), or isValidEmail(). Clear naming improves code readability and helps other developers (or your future self) understand the function's purpose without reading its entire implementation.
  • Use Default Parameters Instead of Manual Checks: Leverage ES6 default parameters (e.g., function greet(name = 'Guest')) instead of manually checking for undefined values inside the function body. This results in cleaner, more concise code and reduces the chances of null or undefined-related bugs.
  • Prefer Arrow Functions for Callbacks, Regular Functions for Methods: Use arrow functions for short callbacks like array methods (map, filter, reduce) where lexical 'this' binding is beneficial. Use regular function syntax for object methods where 'this' should refer to the object itself, avoiding unexpected binding issues.
  • Use Object Parameters for Functions with Many Arguments: Instead of passing many positional parameters, use a single object parameter, such as createUser({name, age, email}). This makes the function call self-descriptive, allows optional parameters to be skipped easily, and reduces argument order errors.
What is the difference between a function declaration and a function expression in JavaScript?
A function declaration is defined using the function keyword with a name at the statement level, and it is fully hoisted, meaning it can be called before its definition in the code. A function expression assigns a function (named or anonymous) to a variable, and it is only available after the line where it is defined, since only the variable declaration is hoisted, not its assignment.
What is the difference between arrow functions and regular functions in terms of 'this' binding?
Regular functions have their own 'this' context, which is determined by how the function is called (e.g., as a method, standalone, or with call/apply/bind). Arrow functions do not have their own 'this'; instead, they inherit 'this' from their enclosing lexical scope at the time they are defined, which makes them unsuitable for defining object methods but ideal for callbacks inside methods.
What is a higher-order function? Give an example.
A higher-order function is a function that either takes one or more functions as arguments, returns a function as its result, or both. Common examples include Array methods like map(), filter(), and reduce(), which accept callback functions as arguments to transform or process array elements, such as [1,2,3].map(num => num * 2) which returns [2,4,6].
Explain closures in JavaScript with an example.
A closure is formed when an inner function retains access to the variables of its outer function's scope even after the outer function has finished executing. For example, function counter() { let count = 0; return function() { count++; return count; }; } creates a closure where the returned inner function keeps access to the count variable, allowing it to maintain state between multiple calls.
What is the difference between synchronous and asynchronous functions in JavaScript?
Synchronous functions execute sequentially, blocking further code execution until the current function completes, such as basic arithmetic or string operations. Asynchronous functions, often implemented using callbacks, promises, or async/await, allow the program to continue executing other code while waiting for operations like API calls, file reads, or timers to complete, improving performance in I/O-heavy applications.
Write a function named isEven that takes a number as a parameter and returns true if the number is even, and false otherwise.
function isEven(num) { return num % 2 === 0; } console.log(isEven(4)); // true console.log(isEven(7)); // false
Create an arrow function named getFullName that takes firstName and lastName as parameters and returns the full name as a single string.
const getFullName = (firstName, lastName) => `${firstName} ${lastName}`; console.log(getFullName('Rahul', 'Sharma')); // 'Rahul Sharma'
Write a function called calculateAverage that accepts an array of numbers and returns their average value.
function calculateAverage(numbers) { const sum = numbers.reduce((total, num) => total + num, 0); return sum / numbers.length; } console.log(calculateAverage([10, 20, 30])); // 20
Create a function called createMultiplier that takes a number x and returns a new function that multiplies its input by x, demonstrating closures.
function createMultiplier(x) { return function(y) { return x * y; }; } const double = createMultiplier(2); console.log(double(5)); // 10 const triple = createMultiplier(3); console.log(triple(5)); // 15
Write a function named delayedGreeting that accepts a name and a callback function, and calls the callback with a greeting message after a 2-second delay using setTimeout.
function delayedGreeting(name, callback) { setTimeout(() => { callback(`Hello, ${name}! Welcome.`); }, 2000); } delayedGreeting('Priya', function(message) { console.log(message); // 'Hello, Priya! Welcome.' after 2 seconds });

Functions are one of the core building blocks of JavaScript, allowing developers to write reusable, modular, and maintainable code. JavaScript supports multiple ways of defining functions, including function declarations, function expressions, and arrow functions, each with unique behaviors around hoisting and 'this' binding. Concepts like default parameters, callback functions, higher-order functions, and closures make JavaScript functions extremely powerful for handling everything from simple calculations to complex asynchronous operations. Mastering functions is essential for writing clean, efficient, and scalable JavaScript applications.

© 2026 CompileX. Maintained by Aditya Kumar Sharma.