Function declarations, expressions and arrow functions
A function is a piece of work with a name, so you can do it again without
writing it again. JavaScript gives you three ways to write one, and unlike most
style choices, the differences are real — one of the three behaves differently
around this, and that difference is the subject of a later lesson.
Declaring one
function orderTotal(plates, rate) {
return plates * rate;
}
console.log(orderTotal(3, 80));
240
plates and rate are parameters — names the function uses internally. The
3 and 80 you pass in are arguments. The distinction matters when you read
error messages.
return hands a value back and stops the function immediately. Anything
after it does not run:
function check(plates) {
if (plates === 0) {
return 'Cancelled.';
}
return `${plates} plates.`;
}
That early return replaces an else, and reads better than one.
A function with no return gives back undefined:
function noReturn() {}
console.log(noReturn());
undefined
This is the cause of a great many "why is it undefined" questions. If a function
seems to produce nothing, check that it actually returns something — a missing
return inside an if is the usual culprit.
Default parameters
function orderTotal(plates, rate = 80) {
return plates * rate;
}
console.log(orderTotal(3));
console.log(orderTotal(3, 95));
240
285
The trap: a default fires for undefined only, not for null.
console.log(orderTotal(3, undefined));
console.log(orderTotal(3, null));
240
0
null is a value, so the default stands aside and 3 * null is 0. Since an
API or a database very often hands you null for "not set", this produces a
free meal rather than an error. Convert at the boundary, exactly as in module 2.
Rest parameters
... collects any number of remaining arguments into an array:
function weekTotal(rate, ...counts) {
let plates = 0;
for (const count of counts) {
plates += count;
}
return plates * rate;
}
console.log(weekTotal(80, 3, 5, 2, 4));
1120
It must be last, and there can only be one.
Function expressions
A function is a value, so it can be assigned to a variable:
const orderTotal = function (plates, rate) {
return plates * rate;
};
That is a function expression. Being a value is the important part — it means a function can be passed to another function, returned from one, or stored in an array. That idea is the last lesson of this module and most of module 4.
Arrow functions
Shorter syntax for the same idea:
const orderTotal = (plates, rate) => {
return plates * rate;
};
When the body is a single expression, drop the braces and the return:
const orderTotal = (plates, rate) => plates * rate;
const double = (n) => n * 2;
console.log(orderTotal(3, 80));
console.log(double(4));
240
8
That implicit return has one sharp edge. To return an object, wrap it in brackets:
const makeOrder = (plates) => ({ plates });
console.log(makeOrder(3));
{ plates: 3 }
Without the brackets, { plates } is read as a function body rather than an
object, and the function returns undefined. The brackets say "this is an
expression, not a block".
Which to use
| Declaration | Expression | Arrow | |
|---|---|---|---|
| Syntax | function f() {} |
const f = function () {} |
const f = () => {} |
| Available before its line | Yes | No | No |
Has its own this |
Yes | Yes | No |
Has arguments |
Yes | Yes | No |
| Usable as a method | Yes | Yes | Rarely — see below |
| Usable as a constructor | Yes | Yes | No |
The practical rule:
- Arrow functions for callbacks — anything passed to
map,filter,addEventListenerorsetTimeout. This is most of what you write. - Declarations for named, top-level functions, because they read clearly and can be called from above.
- Never an arrow for a method on an object that needs
this. The next-but-one lesson shows exactly what goes wrong.
The row that matters most is "has its own this". An arrow function does not get
one — it uses whatever this meant where it was written. That sounds like a
limitation and is in fact the fix for the commonest this bug in JavaScript.
Two lessons from now.
Functions should do one thing
Worth saying early, because it is the habit that makes everything else easier.
function processOrder(plates, rate) {
const total = plates * rate;
console.log(`Total: ₹${total}`);
return total;
}
That function calculates and prints. Now you cannot use it anywhere printing would be wrong — a test, a total of totals, a page that renders rather than logs. Split it:
function orderTotal(plates, rate) {
return plates * rate;
}
console.log(`Total: ₹${orderTotal(3, 80)}`);
A function that returns a value is reusable; a function that prints one is not. Calculate in one place, display in another. The whole of module 7 depends on this separation.
Check your work
orderTotal(3, 80) is 240.
A function with no return gives undefined. So does a function whose
return sits inside an if that did not run — the commonest cause of an
unexpected undefined.
return stops the function. Code after it in the same block never runs.
orderTotal(3, null) is 0, not 240. Default parameters apply only when
the argument is undefined. null is a real value, so the default is skipped
and 3 * null is 0. orderTotal(3, undefined) does give 240.
weekTotal(80, 3, 5, 2, 4) is 1120. The rest parameter collects
[3, 5, 2, 4], which totals 14 plates at ₹80.
const makeOrder = (plates) => { plates }; returns undefined. The braces
are read as a function body containing a pointless expression, not as an object.
=> ({ plates }) returns { plates: 3 }.
Which form for a callback? An arrow. For a method needing this? Not an
arrow.
Practice
- Write
orderTotal(plates, rate)and call it with three different pairs. - Write a function with no
returnand confirm it givesundefined. Then add areturninside anifand call it so theifdoes not run — confirm you getundefinedagain, and note that this is the same bug wearing a disguise. - Write
check(plates)that returns early for zero. Then add a line after the earlyreturnand prove it never runs. - Give
ratea default of80. Then call it withnulland watch the total become zero. Work out why before reading the answer again. - Write
weekTotal(rate, ...counts)and call it with four days, then with none at all. Decide what it should do with none, and make it do that. - Rewrite
orderTotalall three ways — declaration, expression, arrow — and confirm all three give the same answer. - Write an arrow that returns
{ plates, rate }. Get it wrong without the brackets first, see theundefined, then fix it. - Call a function declaration before the line that defines it. It works. Then
try the same with a
constarrow and read the error — the next lesson is entirely about why those differ. - Harder. Write
billLine(item, qty, rate)returning a formatted string likeToor dal x 2 = ₹290, withqtydefaulting to 1. Then writebillTotal(...lines)taking any number of{ qty, rate }objects and returning the sum. Keep the formatting out of the total — that separation is the point of the exercise.
Next: scope and hoisting — why a function can be called before it is written,
and why a const cannot.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship