Functions, parameters and returns
Control flow and functions15 min readLesson 3 of 4
3.1 What a function is
A function packages a block of code under a name so the block can run on demand, with different inputs. It takes zero or more parameters, runs a body, and optionally returns a value. The caller supplies arguments, the values that fill the parameters for that call.
Functions remove repetition and give names to ideas. A well-named function makes the call site readable without opening the body: calculateTotal(prices) explains more than six lines of addition inline. Small functions are also easier to test, because each call is an isolated experiment.
function greet(name) {
return "Hello, " + name + "!";
}
console.log(greet("Aarav"));
console.log(greet("Meera"));Output
Hello, Aarav! Hello, Meera!
A function declaration starts with the function keyword and hoists, so a call can appear before the declaration in the same scope. A function expression assigns the function to a variable and does not hoist in the same way. Arrow functions are a shorter expression form, covered below.
The name after the parameter list is the function's own name. Inside the body, a return statement ends the function immediately and passes a value back. A function without a return, or with a bare return, produces undefined.
3.2 Parameters and arguments
Parameters are the names in the definition; arguments are the values passed in the call. JavaScript does not check the count, so a missing argument becomes undefined and an extra argument is ignored unless the body reads it. Defensive code handles the missing case with a default.
A default parameter runs only when the argument is undefined, not when it is null or 0. A rest parameter collects the remaining arguments into a real array, which is useful for wrapping or for functions that accept a variable number of values.
function total(price, quantity = 1) {
return price * quantity;
}
function sumAll(...numbers) {
return numbers.reduce((sum, number) => sum + number, 0);
}
console.log(total(20));
console.log(total(20, 3));
console.log(sumAll(4, 8, 15));Output
20 60 27
3.3 Return values and early return
A return value is the function's output. Calling a function in an expression uses that value; calling it as a statement discards it. Return as soon as the answer is known. Early returns keep the main case unindented and make the special cases visible at the top.
A function should do one job. If the body needs a comment above each block, split it into helpers. That advice pays off when the function must be tested or reused, because each helper has one behaviour to check.
function shipping(weight, country) {
if (weight <= 0) return "invalid weight";
if (country !== "IN") return "international";
if (weight > 5) return "heavy";
return "standard";
}
console.log(shipping(2, "IN"));
console.log(shipping(8, "IN"));
console.log(shipping(2, "US"));Output
standard heavy international
3.4 Arrow functions
An arrow function is a shorter function expression. It is useful for callbacks, especially when the body is one expression, because the return is implicit. A single parameter needs no parentheses, and the body may omit braces.
Arrow functions do not have their own this. They inherit this from the surrounding scope, which makes them the right choice inside array methods and event callbacks when the outer context matters. A regular function gets its own this, which is usually wrong in those callbacks.
const double = (number) => number * 2;
const prices = [20, 60, 450];
const rounded = prices.map((price) => Math.round(price / 3));
const total = prices.reduce((sum, price) => sum + price, 0);
console.log(double(21));
console.log(JSON.stringify(rounded));
console.log(total);Output
42 [7,20,150] 530
3.5 Functions are values
A function is a value in JavaScript. It can be stored in a variable, passed to another function, returned from a function, and held in an object. A function that accepts another function is a higher-order function, and the passed function is a callback.
This is the foundation for array methods, event listeners and promises. The callback runs when the outer function decides: immediately for map, later for a timer, or on a click for an event listener. Module 2 ends with callbacks and closures, where the timing becomes the main subject.
function repeat(times, action) {
for (let count = 1; count <= times; count += 1) {
action(count);
}
}
repeat(3, (count) => console.log("Pass " + count));Output
Pass 1 Pass 2 Pass 3
3.6 Declarations versus expressions
A function declaration is a statement with the function keyword and a name. It is hoisted, so a call above the declaration works when both are in the same scope. A function expression assigns a function value to a name, and an arrow function is a compact expression. The assigned name is not available before the assignment line.
The practical difference appears when code is loaded in pieces. Declarations are easy to move and read from a distance. Expressions keep the function inside a block or pass it straight into another call. Choose the form that makes the call site clear, not the one that saves the most characters.
A named function expression helps with stack traces: const load = function loadData() {...} shows loadData in an error trace, while an anonymous arrow shows the surrounding variable name.
3.7 Pure functions and side effects
A pure function returns a value that depends only on its arguments, and it does not change anything outside itself. add(a, b) is pure. A function that pushes into a shared array or logs to the console has a side effect, which makes it harder to test.
Most programs need side effects: rendering to the page, saving data, logging. Keep the pure calculation separate from the side effect, then test the calculation without a browser. A function named calculateTotal should return a number; a separate renderTotal can write it to the page.
This split is the practical form of the one-job rule. When a function both computes and renders, a test needs a document, and reuse from another place becomes impossible.
3.8 Naming and splitting functions
A function name is a promise about behaviour. getUser, formatPrice and isValidEmail tell the reader what goes in and what comes out. handleData and process tell almost nothing and invite the function to grow in every direction.
Split a function when it needs a comment to separate two jobs, when its name contains "and", or when testing one behaviour requires setting up another. Small functions compose, while a large function locks the details in place. The split does not need to be permanent; a second use usually reveals the right boundary.
3.9 Common mistakes
- Forgetting return in a function that is supposed to produce a value.
- Using a default parameter and expecting it to replace null.
- Mutating an array parameter when the caller did not expect a change.
- Writing an arrow function for a method that needs its own
this. - Passing a function call instead of the function itself to a callback.
- Building one function with five optional flags instead of several small functions.
Exam
Function questions ask for either the return value or the printed output. Trace each call separately, substitute the arguments, and check whether a missing argument becomes undefined. If a callback is passed, follow the order in which the outer function calls it.
- Write a function that takes a price and a quantity and returns the total.
- Add a default quantity and test both calls.
- Rewrite one regular function as an arrow function.
- Pass a callback to a function you wrote and call it twice.
