---
title: 'Immediately Invoked Functions'
source: 'https://academia.sh/en/courses/javascript-object-model/immediately-invoked-functions'
course: 'Objects and Functions in JavaScript'
language: en
updated: '2026-08-17T18:09:50+00:00'
license: 'CC BY-SA 4.0'
---

# Immediately Invoked Functions

The spellings that turn a function declaration into an expression, scope isolation and the revealing module pattern, one-time setup, and comparison with block scope.

In the previous lesson's old fix for the loop trap, a function was called the instant
it was defined. Its purpose was not to produce a value but to open a new scope. This
pattern's name is the **immediately invoked function expression**.

The pattern does two jobs: it isolates the declarations in its body from the outer
scope, and it hands only selected values back out from the inside. It can also be read
as the Closures lesson's factory functions, run once for a single object.

## Turning a Function Into an Expression

When the parser sees the word `function` at the start of a statement, it treats it as a
**declaration**, and declarations cannot be immediately invoked. This is why the
pattern's wrapping parentheses exist: they move the function out of statement position
and into expression position.

```js
try {
  new Function("function () { return 1; }();");
} catch (error) {
  console.log(`as declaration: ${error.constructor.name}`);
}

const first = (function () {
  return "wrapped call";
})();

const second = (function () {
  return "closed from inside";
}());

const third = (() => "arrow form")();

const fourth = void (function () {
  console.log("converted to expression by operator");
})();

console.log(first);
console.log(second);
console.log(third);
console.log(fourth);
```

```
as declaration: SyntaxError
converted to expression by operator
wrapped call
closed from inside
arrow form
undefined
```

The first attempt shows that the declaration form cannot be parsed; because
`new Function` parses the text, the error can be caught at runtime. The remaining
three forms are valid and behave identically: the call parentheses can sit inside or
outside the wrapper, and an arrow function does the same job.

The fourth form shows forcing expression position with an operator. Because the `void`
operator makes the result `undefined`, it used to be preferred where the return value
is not used. The output order also reveals a detail: the print inside the body runs
before the `console.log(first)` line, because the function is called immediately where
it is defined.

## Scope Isolation and the Module Pattern

The pattern's real use is gathering a group of related functions around shared, hidden
state. What is defined inside is not visible from outside; only the names on the
returned object become accessible. This construction is called the **revealing module
pattern**.

```js
const measurementLog = (function () {
  const records = [];
  let nextOrder = 1;

  function add(sensor, value) {
    const record = { order: nextOrder, sensor, value };
    nextOrder += 1;
    records.push(record);
    return record.order;
  }

  function bySensor(sensor) {
    return records.filter((r) => r.sensor === sensor).length;
  }

  function summary() {
    return `${records.length} records, next order ${nextOrder}`;
  }

  return { add, bySensor, summary };
})();

console.log(measurementLog.add("S-01", 21.4));
console.log(measurementLog.add("S-02", 19.8));
console.log(measurementLog.add("S-01", 22.0));
console.log(measurementLog.bySensor("S-01"));
console.log(measurementLog.summary());
console.log(Object.keys(measurementLog).join(","));
console.log(typeof measurementLog.records);
console.log(JSON.stringify(measurementLog));
```

```
1
2
3
2
3 records, next order 4
add,bySensor,summary
undefined
{}
```

The records array and the order counter cannot be reached from outside; the log's
interface consists of only three functions. That the order number cannot be corrupted
from outside is the preservation of the class invariant introduced in the Programming
Foundations course's Introduction to Object-Oriented Programming lesson — here,
encapsulation is provided by a scope boundary instead of a class.

Where the pattern parts ways with a class is in number: this construction produces a
**single object**. If a second log of the same kind is wanted, it should be used as an
ordinary factory function, not called immediately. Single-instance structures — an
application configuration, a log writer, a counter — are this pattern's natural
territory.

## One-Time Setup

The second common use is preventing the intermediate steps needed to set up a value
from leaking out. The value is computed once; intermediate variables stay inside the
pattern.

```js
const CALIBRATION = (function () {
  const rawLines = ["S-01;-0.4", "S-02;0.15", "S-03;0"];
  const table = Object.create(null);
  for (const line of rawLines) {
    const [sensor, offset] = line.split(";");
    table[sensor] = Number(offset);
  }
  return Object.freeze(table);
})();

console.log(CALIBRATION["S-01"]);
console.log(CALIBRATION["S-02"]);
console.log(Object.isFrozen(CALIBRATION));
console.log(Object.getPrototypeOf(CALIBRATION));
console.log(typeof rawLines);

try {
  CALIBRATION["S-04"] = 1;
} catch (error) {
  console.log(`add: ${error.constructor.name}`);
}
console.log(Object.keys(CALIBRATION).join(","));
```

```
-0.4
0.15
true
null
undefined
add: TypeError
S-01,S-02,S-03
```

The raw lines and the loop variable are never visible in the outer scope at all; what
is left is only a table ready for use. Because the table is built with
`Object.create(null)` it has no chain, and because it is frozen with `Object.freeze`
it cannot be changed afterward — the two decisions from the Immutability Techniques
lesson are applied together here.

This pattern fits constants that have a setup cost. Doing the setup once instead of on
every use is a small-scale example of the preprocessing idea from the Algorithms
course.

## Passing a Dependency In

The pattern's second common use is the wrapper's **parameter list**. If every name
used from outside is passed explicitly, what the body depends on becomes readable, and
name lookup does not reach into outer scopes.

```js
const settings = { threshold: 25 };

const report = (function (source, unit) {
  const measurements = [21.4, 26.8, 23.1];
  const exceeding = measurements.filter((d) => d > source.threshold);
  return `${exceeding.length} measurement exceeded the ${source.threshold}${unit} threshold`;
})(settings, "C");

console.log(report);

(function setupCheck() {
  try {
    null.value;
  } catch (error) {
    const firstLine = error.stack.split("\n")[1].trim().split(" ")[1];
    console.log("name in stack trace:", firstLine);
  }
})();
```

```
1 measurement exceeded the 25C threshold
name in stack trace: setupCheck
```

The second call shows a detail: the wrapper can be given a name. The name is not
accessible from outside — it appears only in the function's own body and in the
**stack trace**. In an unnamed wrapper, the same line is listed as anonymous; the
named form of the pattern is preferred while debugging.

The stack trace's format varies by runtime; here, only the name appearing in the
listing matters.

## Comparison With Block Scope and Modules

Both of the pattern's functions are largely covered by the language's later
abilities. Block-scoped declarations provide isolation; the module system gives a
namespace at the file level.

```js
let blockResult;
{
  const temp = [21.4, 19.8, 25.1];
  blockResult = temp.length;
}
console.log(blockResult);
console.log(typeof temp);

const expressionResult = (() => {
  const temp = [21.4, 19.8, 25.1];
  return temp.reduce((total, d) => total + d, 0) / temp.length;
})();
console.log(expressionResult.toFixed(3));

var scopeTest = "outside";
(function () {
  var scopeTest = "inside";
  console.log(scopeTest);
})();
console.log(scopeTest);
```

```
3
undefined
22.100
inside
outside
```

A block isolates `const` and `let` declarations, and if isolation is the only goal, it
is a plainer path than an immediately invoked function. The second section shows the
difference: a block is a statement, it produces no value; carrying its result out
requires assigning to a variable declared beforehand. An immediately invoked function
is an expression and can be bound directly to a constant.

The third section shows the one case the pattern is still needed for: names declared
with `var` do not enter block scope, they are isolated only by function scope.

The module system, meanwhile, has taken over the namespace job. Every file is its own
scope; no name that is not exported is seen from another file. Reaching for the
pattern to prevent file-level name collisions is no longer necessary. Module system
details are the subject of the Modules, Tooling, and Ecosystem course.

The remaining use cases gather into three headings: setting up a single-instance
interface together with hidden state, isolating a constant's setup steps, and confining
`var` declarations that block scope cannot reach.

## Summary

- Because the word `function` at the start of a statement is parsed as a declaration,
  an immediately invoked function is moved into expression position with a wrapper.
- The pattern opens a new scope; what is defined inside is not visible from outside,
  only the returned value becomes accessible.
- The revealing module pattern produces a single object with hidden state; a factory
  function is used when more than one instance is needed.
- In one-time setup, intermediate variables are isolated and the result is handed out
  as a frozen constant.
- Block scope covers the isolation job but produces no value; the module system takes
  over the namespace job.

## Next Step

Throughout this topic, functions have constantly been used as values: passed as
callbacks, returned from another function, written as a property onto an object,
wrapped to change behavior. All of these uses come from a single ability — a function
also being a value. The next lesson takes up this ability directly and shows, while
building filter, map, and reduce operations on measurement records, where the binding
rules come into play.
