JavaScript · Beginner

JavaScript Functions: Declarations, Arrows and Return Values

10 min readUpdated September 24, 2026Every example verified

In short: A function packages statements under a name so they can run with different inputs. JavaScript writes functions three ways: a function declaration, a function expression and an arrow function. Parameters receive the arguments, return hands a value back to the caller, and a function without a return statement gives undefined.

Why functions exist

A function is a block of statements with a name and a list of parameters. Calling it runs the block with the arguments you pass, and return sends a value back. Functions exist so the same logic is written once and used many times, so a long program can be read as a list of named steps, and so a piece of logic can be tested on its own.

JavaScript offers three ways to write one. A function declaration starts with the keyword function and a name. Declarations are hoisted: the whole function is available from the top of its scope, so you can call it above the line where it is written. A function expression is a function without a name (or with one) stored in a variable; it exists only from that line onward. An arrow function (ES2015) is a shorter expression form: (a, b) => a + b. When the body is a single expression, no braces or return are needed and the expression's value is returned; with braces, the body is ordinary statements and needs an explicit return. Arrow functions also do not get their own this, which matters once you write methods on objects and is covered in the objects lesson.

Parameters are the names in the definition; arguments are the values in the call. If you pass fewer arguments than there are parameters, the missing ones are undefined; pass more, and the extras are ignored. A parameter can have a default, function area(w, h = w), which applies when the argument is missing or undefined. JavaScript does not check argument types, so functions that process input often validate first.

return ends the function immediately and gives the call its value. A function can have several return statements, which is how early exits for special cases are written. If execution reaches the end of the body without a return, the call evaluates to undefined. That is fine for a function whose job is to print, and a bug for one whose result you meant to use.

Variables declared inside a function with let or const are local: they exist only during that call and cannot be seen from outside. A function can read variables from the surrounding scope, which the scope lesson explores. Finally, functions are values. They can be stored in variables and passed to other functions, which is how map and filter receive the code to apply to each element.

Syntax

 JavaScript · syntax
function name(param1, param2 = defaultValue) {   // declaration (hoisted)
  statements;
  return value;                                  // optional; otherwise undefined
}

const name = function (param) { return value; }; // function expression

const name = (param) => expression;              // arrow, implicit return
const name = (param) => { statements; return value; };   // arrow with a body
const name = () => ({ key: 1 });                 // arrow returning an object literal

name(arg1, arg2);                                // call
const result = name(arg1);                       // use the returned value

An arrow body with braces must say return explicitly. Wrap an object literal in parentheses when returning it from a one-line arrow, or the braces are read as a block.

Three ways to write a function

DeclarationExpressionArrow
Usable before its lineyes (hoisted)nono
Own thisyesyesno (uses the surrounding this)
Implicit returnnonoyes, when the body is one expression
Typical usenamed helpers at the top levelstoring a function in a variableshort callbacks passed to other functions

Declare, call and return

A postage function with three price bands and early returns, then a function that prints but returns nothing.

 JavaScript
function postage(weightGrams) {
  if (weightGrams <= 100) {
    return 1.2;
  }
  if (weightGrams <= 500) {
    return 2.9;
  }
  return 5.5;
}

console.log(postage(80));
console.log(postage(250));
console.log(postage(1200));
const total = postage(80) + postage(1200);
console.log("Two parcels:", total);

function greet(name) {
  console.log("Hello, " + name);
}
const result = greet("Nadia");
console.log(result);

Output

1.2
2.9
5.5
Two parcels: 6.7
Hello, Nadia
undefined

Each call to postage runs the body with weightGrams bound to the argument. The first return that executes ends the call, so a parcel of 80 g never reaches the second test, and the final return 5.5 is reached only when both if tests fail. greet prints inside the body and has no return, so the value stored in result is undefined: printing was its side effect, not its result.

Arrow functions, default parameters and functions as values

Two arrows with implicit returns, a default parameter, and functions passed to map and to another function.

 JavaScript
const double = (n) => n * 2;
const area = (w, h = w) => w * h;
console.log(double(21));
console.log(area(3, 4));
console.log(area(5));

console.log([3, 7, 11].map(double));
console.log([3, 7, 11].map((n) => n + 1));

function applyTwice(fn, value) {
  return fn(fn(value));
}
console.log(applyTwice(double, 5));

const describe = function (item, qty) {
  return qty + " x " + item;
};
console.log(describe("chair", 4));
console.log(describe("lamp"));

Output

42
12
25
[ 6, 14, 22 ]
[ 4, 8, 12 ]
20
4 x chair
undefined x lamp

double and area have single-expression bodies, so the expression is the return value. area(5) supplies only w, and the default h = w fills in 5, giving a square. map calls the function you give it once per element and collects the results; passing double by name (no parentheses) hands over the function itself rather than calling it. applyTwice receives a function as its first parameter and calls it twice. The last call to describe omits qty, so the parameter is undefined and is joined into the string as the word undefined.

Validating input with small functions and early returns

The input lines are 72, abc, 95 and 30. One function parses, another classifies, and a local variable stays local.

 JavaScript
function toScore(text) {
  const n = Number(text);
  if (text.trim() === "" || Number.isNaN(n)) {
    return null;
  }
  return n;
}

function label(score) {
  if (score === null) return "invalid";
  if (score >= 90) return "distinction";
  if (score >= 50) return "pass";
  return "fail";
}

let line;
while ((line = readline()) !== null) {
  const score = toScore(line);
  console.log(line + ": " + label(score));
}

function count() {
  let hidden = 1;
  return hidden + 1;
}
console.log(count());
console.log(typeof hidden);

Input given to the program: 72abc9530

Output

72: pass
abc: invalid
95: distinction
30: fail
2
undefined

Splitting the work into toScore and label means each function answers one question and the loop reads like a sentence. toScore returns null for bad input, and label checks for that first, so the two agree on how "invalid" is represented. hidden is declared inside count, so after the call it does not exist outside; typeof reports undefined rather than throwing.

Common mistakes

  • Computing a value but forgetting to return it

    Why it goes wrong: function tax(amount) { amount * 0.2; } evaluates the multiplication and throws the result away, so tax(50) is undefined and any arithmetic on it gives NaN.

    Fix: Write return in front of the value the caller needs.

     JavaScript · fix
    function tax(amount) {
      return amount * 0.2;
    }
    console.log(tax(50)); // 10
  • Calling a function when you meant to pass it, or the reverse

    Why it goes wrong: numbers.map(double()) calls double with no argument (giving NaN) and hands that number to map, which expected a function. console.log(double) without parentheses prints the function rather than a result.

    Fix: Use the bare name to refer to a function and add parentheses to call it.

  • An arrow function with braces but no return

    Why it goes wrong: const double = (n) => { n * 2; }; has a block body, so nothing is returned and every call gives undefined. The same trap catches () => { key: 1 }, which is a block containing a label, not an object.

    Fix: Drop the braces for a single expression, add return inside them, or wrap an object literal in parentheses: () => ({ key: 1 }).

     JavaScript · fix
    const double = (n) => n * 2;
    const make = () => ({ key: 1 });
  • Calling a function expression above its definition

    Why it goes wrong: Only declarations are hoisted. const helper = () => 1; written below a call to helper() throws ReferenceError: Cannot access 'helper' before initialization.

    Fix: Define expressions and arrows before the first call, or use a function declaration for helpers you want to call from anywhere in the file.

Where you use this

Exercises get easier the moment you separate reading input from computing the answer. A function that takes plain values and returns a result can be checked in your head with a few example arguments, while the loop that reads lines stays short and obvious. The same separation is what makes larger programs testable. Small functions with descriptive names also replace comments; if (isLeapYear(year)) explains itself where the raw arithmetic would not.

 JavaScript · in practice
function isLeapYear(year) {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}
const year = Number(readline());
console.log(isLeapYear(year) ? "leap" : "common");

Key points

  • Declare with function name() {}, or store an expression or arrow (a) => a * 2 in a const.
  • Declarations are hoisted; expressions and arrows are usable only after their line.
  • return ends the call and supplies its value; no return means undefined.
  • Missing arguments are undefined; give a parameter a default with = value.
  • A one-expression arrow returns implicitly; a braced body needs return.
  • Functions are values: pass them by name to map, filter and your own functions.

Try it yourself

Complete clamp so it returns low when the value is below it, high when the value is above it, and the value itself otherwise. The three calls should print 10, 0 and 7.

Your program
function clamp(value, low, high) {
  // return low, high or value
}

console.log(clamp(15, 0, 10));
console.log(clamp(-3, 0, 10));
console.log(clamp(7, 0, 10));
Expected output: 10 0 7

Practise this

Open the JavaScript playground

Frequently asked questions

What is the difference between a function declaration and an arrow function?

A declaration (function f() {}) is hoisted, so it can be called before the line where it appears, and it has its own this. An arrow function is an expression assigned to a variable, exists only from that line on, has no this of its own (it uses the surrounding one) and can return a single expression without braces or return. Use declarations for named helpers and arrows for short callbacks.

What happens if I call a JavaScript function with the wrong number of arguments?

Nothing is reported. Parameters without a matching argument are undefined, and extra arguments are ignored (they remain reachable through the rest parameter syntax in the spread and rest lesson). Give parameters defaults when a missing value has a sensible meaning, and validate inside the function when it does not.

Can a JavaScript function return more than one value?

A function returns exactly one value, but that value can be an array or an object holding several. return [min, max] or return { min, max } is the usual approach, and the caller can unpack it with destructuring: const [low, high] = range(values).

Progress is stored only in this browser.

How this page was checked. Every program on it was run with Node.js 22 at build time; runs in your browser in an isolated Web Worker by the publishing checks, and the output shown is what it printed. The Run buttons execute the same code on your device; nothing you type is sent anywhere.