RizTech Academy logo
RizTech Academy
Functions, Scope and ClosuresLesson 1 of 525 min

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, addEventListener or setTimeout. 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

  1. Write orderTotal(plates, rate) and call it with three different pairs.
  2. Write a function with no return and confirm it gives undefined. Then add a return inside an if and call it so the if does not run — confirm you get undefined again, and note that this is the same bug wearing a disguise.
  3. Write check(plates) that returns early for zero. Then add a line after the early return and prove it never runs.
  4. Give rate a default of 80. Then call it with null and watch the total become zero. Work out why before reading the answer again.
  5. 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.
  6. Rewrite orderTotal all three ways — declaration, expression, arrow — and confirm all three give the same answer.
  7. Write an arrow that returns { plates, rate }. Get it wrong without the brackets first, see the undefined, then fix it.
  8. Call a function declaration before the line that defines it. It works. Then try the same with a const arrow and read the error — the next lesson is entirely about why those differ.
  9. Harder. Write billLine(item, qty, rate) returning a formatted string like Toor dal x 2 = ₹290, with qty defaulting to 1. Then write billTotal(...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