Module: JavaScript
JavaScript·049·6 MIN READ

049: Functions I: Declarations, Expressions, Parameters, and Returns

TOPICS COVERED: Functions I: Declarations, Expressions, Parameters, and Returns

Outcomes

By the end of this lesson, you can:

  • define and call a function declaration;
  • distinguish parameters from arguments;
  • return a value and use it at the call site;
  • decompose repeated logic into readable functions;
  • validate inputs with guard returns; and
  • describe a pure function as predictable input-to-output logic without external side effects.

Prerequisites and Retrieval

Retrieve variables, conditions, and loops.

  1. Why is const the default declaration?
  2. How can a guard condition reject an invalid score?
  3. What value does an accumulator hold after a loop?

Today, prefer function results over logging inside calculation functions. Log at the outer call site so logic remains reusable.

Terms

  • function: A reusable procedure invoked to perform a task and optionally return a value. — Source: MDN: Functions
  • function declaration: function name() {} syntax defining a named hoisted function. — Source: MDN: Functions
  • function body: The braced statements executed on call. — Source: MDN: Functions
  • parameter: Named placeholder in the definition receiving a call-time value. — Source: MDN: Glossary — Parameter
  • argument: Actual value passed into a call. — Source: MDN: Glossary — Argument
  • call/invocation: Executing a function via name(args), transferring control. — Source: MDN: Functions
  • return value: The value delivered back to the caller by return. — Source: MDN: return
  • call site: Location in code where the function is invoked. — Source: MDN: Functions
  • guard return: Early return exiting when inputs invalid before main logic. — Source: MDN: Functions
  • pure function: Same output for same input with no side effects. — Source: MDN: Glossary — Pure function (see Function)
  • side effect: Observable change outside the function (mutation, I/O, logging). — Source: MDN: Functions
  • Function (official): "A function is a callable object that executes a block of code." — Source: MDN: Functions
  • Parameter vs Argument (official): "Parameters are names in the function definition; arguments are values passed at call time." — Source: MDN: Functions — Parameters
  • Return value (official): "The return statement specifies the value to be returned by a function." — Source: MDN: return

Beginner Explanation and Mental Model

A function is a small machine with a name. Arguments go in, statements process them, and a return value comes out. Define the machine once, then call it with different inputs:

js
function add(firstNumber, secondNumber) {
  return firstNumber + secondNumber;
}

const total = add(4, 6);
console.log(total); // 10

firstNumber and secondNumber are parameters. They are local names available during each call. 4 and 6 are arguments. Defining a function does not run its body. add(4, 6) performs the call.

When execution reaches return, the current function stops and sends a value to its caller. Code after an unconditional return is unreachable. Without a return statement, a function returns undefined:

js
function showMessage() {
  console.log("Hello");
}

const result = showMessage(); // logs Hello
console.log(result);           // undefined

Logging and returning solve different problems. Logging helps a person observe a value. Returning gives a value to other code. A calculator should normally return its answer; the caller chooses whether to log it, display it, store it, or compare it.

Input, processing, output

Design a function by writing its contract in plain language:

  • input: two number arguments;
  • processing: multiply them;
  • output: product number.

Then name it with a verb or verb phrase:

js
function calculateArea(width, height) {
  return width * height;
}

Function declarations are available throughout their containing scope, but define before use in beginner code because top-to-bottom reading is clearer.

Pure functions

js
function applyDiscount(price, rate) {
  return price - (price * rate);
}

For the same arguments this returns the same result, and it changes no outside state. That makes it easy to test. Not every function can be pure, because applications eventually update interfaces and communicate with systems. Keep calculation and validation logic pure when practical, then perform effects at clear boundaries.

Worked Example: Reusable Price Calculator

js
function calculateSubtotal(unitPrice, quantity) {
  return unitPrice * quantity;
}

function calculateDiscount(subtotal, discountRate) {
  return subtotal * discountRate;
}

function calculateFinalTotal(unitPrice, quantity, discountRate) {
  const subtotal = calculateSubtotal(unitPrice, quantity);
  const discount = calculateDiscount(subtotal, discountRate);
  return subtotal - discount;
}

const firstTotal = calculateFinalTotal(200, 3, 0.10);
const secondTotal = calculateFinalTotal(150, 2, 0);

console.log("First total:", firstTotal);
console.log("Second total:", secondTotal);

Expected output:

text
First total: 540
Second total: 300

Trace the first call. Its parameters receive 200, 3, and 0.10. The subtotal function returns 600. The discount function receives 600 and returns 60. The final function returns 540. Each helper has one clear purpose, while calculateFinalTotal composes the results.

The functions do not log. This permits:

js
const isWithinBudget = calculateFinalTotal(200, 3, 0.10) <= 550;
console.log(isWithinBudget); // true

If the calculation only logged, it could not participate in this comparison.

Intermediate Example: Validation With Guard Returns

js
function isValidScore(score) {
  if (typeof score !== "number") {
    return false;
  }

  if (Number.isNaN(score)) {
    return false;
  }

  return score >= 0 && score <= 100;
}

function getGrade(score) {
  if (!isValidScore(score)) {
    return "Invalid score";
  }

  if (score >= 90) {
    return "A";
  }
  if (score >= 75) {
    return "B";
  }
  if (score >= 60) {
    return "C";
  }
  return "F";
}

console.log(getGrade(84));
console.log(getGrade(105));
console.log(getGrade("84"));

Expected output:

text
B
Invalid score
Invalid score

The validator answers one Boolean question. Guards make invalid inputs leave immediately. In getGrade, each successful threshold returns, so else is unnecessary. This flat shape is easier to scan than deeply nested branches. The function does not silently coerce "84"; callers must provide the contract's number input.

Optional Advanced Extension: Default Parameters

A default parameter applies when an argument is missing or explicitly undefined:

js
function greetUser(name, greeting = "Hello") {
  return `${greeting}, ${name}!`;
}

console.log(greetUser("Ilan"));
console.log(greetUser("Ilan", "Welcome"));

Expected output:

text
Hello, Ilan!
Welcome, Ilan!

Defaults belong in the parameter list, not in truthy fallbacks that accidentally replace valid 0 or empty-string arguments. Use defaults only when omission has a clear meaning.

Deep Dive: Function Forms, Parameters, IIFEs, and arguments

JavaScript has several function forms with different semantics.

js
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

const formatCurrency = function (amount) {
  return `₹${amount.toFixed(2)}`;
};

const isPositive = (value) => value > 0;

Default and rest parameters

js
function createOrder(customer = "Walk-in", ...items) {
  return { customer, items };
}

Rest parameters are real arrays. Prefer them over the legacy arguments object in new code.

The arguments object

Traditional functions expose an array-like arguments object.

js
function showArguments() {
  console.log(arguments.length);
  console.log(arguments[0]);
}

showArguments("a", "b");

Arrow functions do not have their own arguments.

IIFE

An Immediately Invoked Function Expression executes as soon as it is created.

js
(() => {
  const privateValue = 42;
  console.log(privateValue);
})();

IIFEs were historically used to create private scope before ES modules became standard. You should understand them for legacy code, while preferring modules for modern application boundaries.

Mistakes and Debugging

  • Defining but not calling: calculateTotal refers to the function; calculateTotal() invokes it.
  • Logging instead of returning: callers receive undefined. Return the data, then log the call result.
  • Forgetting to use the result: assign it, log it, compare it, or pass it onward.
  • Parameter/argument confusion: parameters are definition names; arguments are call values.
  • Missing path return: if some branches return and another falls through, unexpected undefined appears.
  • Code after return: it never executes.
  • Wrong argument order: named parameters receive arguments by position. Keep related signatures simple.
  • Changing outer variables from a calculator: return a result instead to reduce hidden dependencies.
  • Silently coercing invalid inputs: define and enforce a clear contract.

Debug by calling a function with one small known input, logging the returned value and type, and testing boundary/invalid cases. If a result is undefined, inspect every control-flow path for a return.

Best Practices

  • Give functions verb-based names that describe their output or action.
  • Keep each function focused on one responsibility.
  • Return calculated data rather than logging inside reusable logic.
  • Use parameters instead of reading globals.
  • Prefer pure functions for calculation and validation.
  • Validate at clear boundaries and return early for invalid inputs.
  • Avoid mutating argument objects unless the function name and contract make that explicit.
  • Keep parameter counts manageable; do not add speculative options.
  • Define a function before its first call for readable example code.

Tiered Exercises

Core

Write subtract(a, b), multiply(a, b), and isEven(number). Return results and log calls with at least two sets of arguments.

Practice

Write functions for rectangle area and perimeter. Add isValidDimension(value) that accepts finite positive numbers. Return "Invalid dimensions" when either dimension fails validation.

Professional Extension

Write calculateTicketPrice(age, basePrice). Reject invalid ages/prices. Under 12 receives 50% off, age 60 or above receives 25% off, and others pay full price. Use small validation and discount functions.

Complete Solutions

js
function subtract(a, b) {
  return a - b;
}

function multiply(a, b) {
  return a * b;
}

function isEven(number) {
  return number % 2 === 0;
}

console.log(subtract(10, 3)); // 7
console.log(subtract(5, 8));  // -3
console.log(multiply(4, 6));  // 24
console.log(multiply(2, 9));  // 18
console.log(isEven(12));      // true
console.log(isEven(7));       // false
js
function isValidDimension(value) {
  return typeof value === "number" && Number.isFinite(value) && value > 0;
}

function calculateArea(width, height) {
  if (!isValidDimension(width) || !isValidDimension(height)) {
    return "Invalid dimensions";
  }
  return width * height;
}

function calculatePerimeter(width, height) {
  if (!isValidDimension(width) || !isValidDimension(height)) {
    return "Invalid dimensions";
  }
  return 2 * (width + height);
}

console.log(calculateArea(5, 3));      // 15
console.log(calculatePerimeter(5, 3)); // 16
console.log(calculateArea(-1, 3));     // Invalid dimensions
js
function isValidNonNegativeNumber(value) {
  return typeof value === "number" && Number.isFinite(value) && value >= 0;
}

function getTicketDiscountRate(age) {
  if (age < 12) {
    return 0.50;
  }
  if (age >= 60) {
    return 0.25;
  }
  return 0;
}

function calculateTicketPrice(age, basePrice) {
  if (!isValidNonNegativeNumber(age) || !isValidNonNegativeNumber(basePrice)) {
    return "Invalid input";
  }
  const rate = getTicketDiscountRate(age);
  return basePrice - (basePrice * rate);
}

console.log(calculateTicketPrice(10, 200)); // 100
console.log(calculateTicketPrice(65, 200)); // 150
console.log(calculateTicketPrice(30, 200)); // 200

Recap and Exit Questions

Functions package reusable logic. Parameters describe inputs, arguments supply call values, and return sends output. Pure functions make input-output relationships explicit, while guard returns keep invalid cases separate.

  1. How do defining and calling differ?
  2. How do parameters and arguments differ?
  3. Why is returning more reusable than logging?
  4. What happens when execution reaches return?
  5. What makes a calculation function pure?

Official References

References checked 2026-08-24.