An immediately invoked function expression is a function expression that is called right where it is written: (function () { ... })() or (() => { ... })().
Why the parentheses. A statement cannot start with the token function. The grammar for ExpressionStatement has a lookahead restriction against {, function, async function, class, and let [, so function () {}() is parsed as a declaration, which needs a name and can't be called. Wrapping in (...), or any operator that forces expression context (!, void, =), makes it an expression. The expression position removes the ambiguity.
What it gave you. Calling a function creates a new function scope, so vars and helper functions inside stay private, and the closure lets the IIFE return an API (the module pattern). That solved global-namespace pollution in classic scripts before modules existed.
Do you still need one? Mostly no:
- Private scope:
{ let x }blocks, or an ES module, where top-level declarations are module-scoped. - Private state: a module-level variable, a factory function, or a class
#privatefield. - Per-iteration capture in loops:
letinforcreates a binding per iteration.
Still useful: an async IIFE to use await in a context without top-level await (CommonJS, older scripts), and a one-off initialisation that needs statements but must produce a const value.