JavaScript · Beginner
JavaScript Functions: Declarations, Arrows and Return Values
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
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 valueAn 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
| Declaration | Expression | Arrow | |
|---|---|---|---|
| Usable before its line | yes (hoisted) | no | no |
| Own this | yes | yes | no (uses the surrounding this) |
| Implicit return | no | no | yes, when the body is one expression |
| Typical use | named helpers at the top level | storing a function in a variable | short 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.
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.
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.
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: 72 ↵ abc ↵ 95 ↵ 30
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, sotax(50)isundefinedand any arithmetic on it givesNaN.Fix: Write
returnin front of the value the caller needs.JavaScript · fixfunction tax(amount) { return amount * 0.2; } console.log(tax(50)); // 10Calling a function when you meant to pass it, or the reverse
Why it goes wrong:
numbers.map(double())callsdoublewith no argument (givingNaN) and hands that number tomap, 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 givesundefined. 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
returninside them, or wrap an object literal in parentheses:() => ({ key: 1 }).JavaScript · fixconst 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 tohelper()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.
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 * 2in aconst. - Declarations are hoisted; expressions and arrows are usable only after their line.
returnends the call and supplies its value; noreturnmeansundefined.- 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,filterand 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.
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));
10
0
7function clamp(value, low, high) {
if (value < low) {
return low;
}
if (value > high) {
return high;
}
return value;
}
console.log(clamp(15, 0, 10));
console.log(clamp(-3, 0, 10));
console.log(clamp(7, 0, 10));
Practise this
Open the JavaScript playground
Related lessons
- Loops in JavaScript: for, while, for...of and for...infor, while, do...while, for...of and for...in loops in JavaScript, with break and continue, and how to read every line of input in a loop.9 min
- JavaScript Arrays: Creating, Indexing and Changing ListsHow JavaScript arrays store ordered lists: indexing, length, push and pop, slice versus splice, sorting numbers correctly, and map, filter and reduce.10 min
- Scope and Hoisting in JavaScriptHow JavaScript decides where a variable is visible: global, function and block scope, how var and function declarations hoist, and why let has a dead zone.10 min
- Closures in JavaScriptWhat a JavaScript closure is, how a function keeps access to the scope it was created in, and how to use that for counters, factories and callbacks.10 min
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).
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.