---
title: 'Function Declaration and Expression'
source: 'https://academia.sh/en/courses/javascript-fundamentals/function-declarations-and-expressions'
course: 'JavaScript Fundamentals'
language: en
updated: '2026-08-23T07:00:57+00:00'
license: 'CC BY-SA 4.0'
---

# Function Declaration and Expression

The hoisting difference between the two definition forms, named function expressions, using functions as values, return rules, and the automatic semicolon insertion trap.

The previous lesson had checks and accumulators embedded in the loop body. Doing the
same work a second time would mean copying code. The Programming Fundamentals course's
Function Definition and Invocation lesson established the reason for this separation.

JavaScript has more than one way to define a function, and the difference between them
is not just syntax. This lesson compares the two basic forms; the third form, arrow
functions, is covered in this topic's fifth lesson.

## Two Definition Forms

A **function declaration** is a statement that begins with the `function` keyword and
carries a name. A **function expression** is a form that produces a value and is
usually assigned to a variable.

The difference shown in the Naming and Scope Rules lesson repeats here: a declaration
is hoisted together with its body, an expression is not.

```js
console.log(averageDeclaration([21.5, 19.75]));

function averageDeclaration(array) {
  let total = 0;
  for (const value of array) total += value;
  return total / array.length;
}

const averageExpression = function (array) {
  let total = 0;
  for (const value of array) total += value;
  return total / array.length;
};
console.log(averageExpression([21.5, 19.75]));
```

```
20.625
20.625
```

The declaration could be called from the line before its definition. Had the
expression been called before its assignment line, it would have thrown a temporal
dead zone error.

The second difference between the two forms is that the expression is a **value**: it
can be passed directly as an argument, made a property of an object, or returned from a
function. A declaration cannot do this; it first has to be bound to a name.

This course's rule: **helper functions in the outermost scope are written in
declaration form**; functions passed to another function or carried in a variable are
written in expression form. The reason is that a declaration is independent of call
order and frees up the file's reading order.

## Named Function Expressions

A function expression may carry a name. This name is seen only inside the function's
**own body**:

```js
const sum = function adder(array, index = 0) {
  if (index >= array.length) return 0;
  return array[index] + adder(array, index + 1);
};
console.log(sum([21.5, 19.75, 23]));
console.log(sum.name);
try {
  console.log(adder([1]));
} catch (error) {
  console.log(error.name + ": " + error.message);
}
```

```
64.25
adder
ReferenceError: adder is not defined
```

The inner name made the recursive call possible and showed up in the `name` property,
but it was not accessible from outside. This lets a recursive function expression call
itself without depending on the outer variable; even if the outer variable later gets
bound to another value, the recursion keeps working.

Functions carry two informative properties:

```js
function aboveThreshold(array, threshold) { return array.filter((d) => d > threshold); }
const anonymous = function (a, b) { return a + b; };
console.log(aboveThreshold.name, aboveThreshold.length);
console.log(anonymous.name, anonymous.length);
console.log(typeof aboveThreshold, aboveThreshold instanceof Object);
```

```
aboveThreshold 2
anonymous 2
function true
```

`name` is the name that appears in error messages and stack traces; it can be inferred
from the variable it is assigned to. `length` is the expected parameter count —
parameters with defaults and rest parameters are not counted, which is the next
lesson's subject.

The last line is a type observation: functions are objects. `typeof` gives a distinct
result, but they carry properties and behave as objects.

## Functions Are Values

A function being a value is what makes passing it as an argument and returning it from
a function possible. This is the higher-order function concept defined in the
Programming Fundamentals course's Introduction to Functional Programming lesson.

```js
const temperatures = [21.5, 19.75, 23, 18.25];

function thresholdFilter(threshold) {
  return function (value) {
    return value > threshold;
  };
}

const above20 = thresholdFilter(20);
console.log(temperatures.filter(above20));
console.log(temperatures.filter(thresholdFilter(22)));
```

```
[ 21.5, 23 ]
[ 23 ]
```

The returned function keeps carrying the `threshold` binding from the call it was
created in. Two separate calls produced two separate bindings; the first saw `20`, the
second `22`. This behavior is called a **closure** and is one of the core subjects of
the next course. Here it is only being used.

A function expression can be called immediately where it is defined. This pattern is
used to do work without leaking a name to the outer scope:

```js
const report = (function () {
  const hidden = [21.5, 19.75];
  return { count: hidden.length };
})();
console.log(report);
console.log(typeof hidden);
```

```
{ count: 2 }
undefined
```

The name `hidden` is not visible outside. Module mode removes most of this need — as
seen in the Ways to Run Code lesson, a module's outermost scope is already private —
but the pattern is still used to open a narrow scope.

## Declarations Inside a Block

In strict mode, function declarations are **block-scoped**:

```js
if (true) {
  function inBlock() {
    return "block";
  }
  console.log(inBlock());
}
try {
  console.log(inBlock());
} catch (error) {
  console.log(error.name + ": " + error.message);
}
```

```
block
ReferenceError: inBlock is not defined
```

This behavior is different in non-strict scripts, and compatibility rules come into
play. The way to avoid ambiguity is to write conditional definitions as expressions
rather than declarations: a variable declaration's scope rule is the same in both
modes.

## Return Rules

A function with no return value written returns `undefined`. This is the value the
caller sees, independent of whether the function did any work.

```js
function logRecord(record) {
  console.log(record.station, record.temperature);
}
const result = logRecord({ station: "A1", temperature: 21.5 });
console.log("return:", result);

function earlyExit(value) {
  if (value == null) return;
  return value * 2;
}
console.log(earlyExit(null), earlyExit(10));
```

```
A1 21.5
return: undefined
undefined 20
```

The command–query separation from the Programming Fundamentals course applies here too:
a function either does work or returns a value. `logRecord` does work; using its return
value is meaningless.

## The Automatic Semicolon Insertion Trap

JavaScript inserts a semicolon at line end in certain situations. The rule has a sharp
consequence for the `return` statement: **no line break may sit between `return` and
the expression to be returned.**

```js
function brokenReturn() {
  return
    21.5;
}
function correctReturn() {
  return 21.5;
}
console.log(brokenReturn());
console.log(correctReturn());
```

```
undefined
21.5
```

In the first function, the `return` statement was terminated on its own line; the
`21.5;` line became a separate statement that never runs. No error was thrown, no
warning appeared — only the wrong value came back.

The same rule applies to `throw`, `break`, `continue`, and the increment operators. The
way to guard against it is to start the expression to be returned on the same line as
`return`. If a long expression is being returned, its opening parenthesis is placed on
the same line.

## Applying It to the Measurement Script

Once the previous lessons' checks and computations are split into functions, the
script's body shrinks to a few lines showing the flow:

```js
function isValid(record) {
  return record.temperature != null && !Number.isNaN(record.temperature);
}

function average(records) {
  let total = 0;
  let count = 0;
  for (const record of records) {
    if (!isValid(record)) continue;
    total += record.temperature;
    count += 1;
  }
  return count === 0 ? null : total / count;
}

const records = [
  { station: "A1", temperature: 21.5 },
  { station: "A2", temperature: NaN },
  { station: "B1", temperature: 23 },
  { station: "B2", temperature: null },
];

console.log("valid:", records.filter(isValid).length);
console.log("average:", average(records));
console.log("empty list:", average([]));
```

```
valid: 2
average: 22.25
empty list: null
```

`isValid` was both passed as an argument to `filter` and called inside a loop — the
direct benefit of a function being a value. The `average` function returns `null` for
an empty list; had it divided anyway, it would have produced `NaN` and the error would
have moved forward silently.

## Summary

- A function declaration is hoisted together with its body; a function expression comes
  into being on its assignment line and is in the dead zone before that.
- A named function expression's name is seen only inside its own body and makes
  recursion independent of the outer variable.
- Functions are values: they are passed as arguments, returned, and carry properties as
  objects.
- In strict mode, function declarations inside a block are block-scoped; conditional
  definitions are written as expressions.
- A function with no return value written gives `undefined`; if a line break sits
  between `return` and the expression, the statement ends there and `undefined` is
  returned silently.

## Next Step

In this lesson, functions were called with a fixed number of parameters. In real usage,
an argument may be missing, extra, or its count may not be known in advance. The next
lesson covers parameter rules: when default values kick in, how rest parameters
collect, and why the old arguments object is not used in newer code.
