Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve IIFE docs #37361

Merged
merged 2 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions files/en-us/_redirects.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3597,6 +3597,7 @@
/en-US/docs/Glossary/SSL_Glossary /en-US/docs/Glossary/SSL
/en-US/docs/Glossary/Scrollport /en-US/docs/Glossary/Scroll_container
/en-US/docs/Glossary/Second-level_Domain /en-US/docs/Glossary/SLD
/en-US/docs/Glossary/Self-Executing_Anonymous_Function /en-US/docs/Glossary/IIFE
/en-US/docs/Glossary/Serialize /en-US/docs/Glossary/Serialization
/en-US/docs/Glossary/Simple_header /en-US/docs/Glossary/CORS-safelisted_request_header
/en-US/docs/Glossary/Simple_response_header /en-US/docs/Glossary/CORS-safelisted_response_header
Expand Down
4 changes: 0 additions & 4 deletions files/en-us/_wikihistory.json
Original file line number Diff line number Diff line change
Expand Up @@ -4495,10 +4495,6 @@
"modified": "2020-10-05T12:31:04.165Z",
"contributors": ["alattalatta", "darby", "klez", "devanshmanu", "jswisher"]
},
"Glossary/Self-Executing_Anonymous_Function": {
"modified": "2019-09-24T05:50:18.861Z",
"contributors": ["natevw", "Porkepix", "chrisdavidmills"]
},
"Glossary/Semantics": {
"modified": "2020-11-27T04:59:07.153Z",
"contributors": [
Expand Down
146 changes: 17 additions & 129 deletions files/en-us/glossary/iife/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,155 +6,43 @@ page-type: glossary-definition

{{GlossarySidebar}}

An **IIFE** (Immediately Invoked Function Expression) is a {{glossary("JavaScript")}} {{glossary("function")}} that runs as soon as it is defined.
Copy link
Contributor Author

@kirkwaiblinger kirkwaiblinger Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first read, this sounds to me as a reader like

function implicitlyRunsImmediatelyForSomeReason() {}

rather than an explicit pattern.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks accurate.

The name IIFE is promoted by Ben Alman in [his blog](https://web.archive.org/web/20171201033208/http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife).
An **IIFE** (Immediately Invoked Function Expression) is an idiom in which a {{glossary("JavaScript")}} {{glossary("function")}} runs as soon as it is defined. It is also known as a _self-executing anonymous function_. The name IIFE is promoted by Ben Alman in [his blog](https://web.archive.org/web/20171201033208/http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife).

```js
// standard IIFE
(function () {
// …
// statements
})();

// arrow function variant
(() => {
// …
// statements
})();

// async IIFE
(async () => {
// …
// statements
})();
```

It is a design pattern which is also known as a {{glossary("Self-Executing Anonymous Function")}} and contains two major parts:
It contains two major parts:

1. The first is the anonymous function with lexical scope enclosed within the [grouping operator](/en-US/docs/Web/JavaScript/Reference/Operators/Grouping) `()`. This prevents accessing variables within the IIFE idiom as well as polluting the global scope.
2. The second part creates the immediately invoked function expression `()` through which the JavaScript engine will directly interpret the function.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was particularly incomprehensible to me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed since it doesn't have to be anonymous or even enclosed within (). And the scope the function expression creates does not belong to the enclosing ():

const [uncleDavidStr, uncleDavidFunc] = function uncleDavidFunc() {
    return ['Uncle David', uncleDavidFunc];
}();
(uncleDavidFunc, function uncleDavidFunc() {
})(); // ReferenceError: uncleDavidFunc is not defined

1. A [function _expression_](/en-US/docs/Web/JavaScript/Reference/Operators/function). This usually needs to be [enclosed in parentheses](/en-US/docs/Web/JavaScript/Reference/Operators/Grouping) in order to be parsed correctly.
2. Immediately _calling_ the function expression. Arguments may be provided, though IIFEs without arguments are more common.

## Use cases
IIFEs are a common pattern used to execute arbitrarily many statements in their own scope (and possibly return a value), in a location that requires a single expression. They are similar to, but much more powerful than, the [comma operator](/en-US/docs/Web/JavaScript/Reference/Operators/Comma_operator), which can only execute multiple expressions and, therefore, does not provide a way to use local variables or control flow statements.

### Avoid polluting the global namespace
Use cases of IIFEs include:

Because our application could include many functions and global variables from different source files, it's
important to limit the number of global variables. If we have some initiation code that we don't need to use
again, we could use the IIFE pattern. As we will not reuse the code again, using IIFE in this case is better than
using a function declaration or a function expression.
- Avoiding polluting the global namespace by creating a new {{glossary("scope")}}).
- Creating a new async context to use {{jsxref("Operators/await", "await")}} in a non-async context.
- Computing values with complex logic, such as using multiple statements as a single expression.

```js
(() => {
// some initiation code
let firstVariable;
let secondVariable;
})();

// firstVariable and secondVariable will be discarded after the function is executed.
```

### Execute an async function

An [`async`](/en-US/docs/Web/JavaScript/Reference/Operators/async_function) IIFE allows you to use [`await`](/en-US/docs/Web/JavaScript/Reference/Operators/await) and [`for-await`](/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) even in older browsers and JavaScript runtimes that have no [top-level await](/en-US/docs/Web/JavaScript/Reference/Operators/await#top_level_await):

```js
const getFileStream = async (url) => {
// implementation
};

(async () => {
const stream = await getFileStream("https://domain.name/path/file.ext");
for await (const chunk of stream) {
console.log({ chunk });
}
})();
```

### The module pattern

We would also use IIFE to create private and public variables and methods. For a more sophisticated use of the module
pattern and other use of IIFE, you could see the book Learning JavaScript Design Patterns by Addy Osmani.

```js
const makeWithdraw = (balance) =>
((copyBalance) => {
let balance = copyBalance; // This variable is private
const doBadThings = () => {
console.log("I will do bad things with your money");
};
doBadThings();
return {
withdraw(amount) {
if (balance >= amount) {
balance -= amount;
return balance;
}
return "Insufficient money";
},
};
})(balance);

const firstAccount = makeWithdraw(100); // "I will do bad things with your money"
console.log(firstAccount.balance); // undefined
console.log(firstAccount.withdraw(20)); // 80
console.log(firstAccount.withdraw(30)); // 50
console.log(firstAccount.doBadThings); // undefined; this method is private
const secondAccount = makeWithdraw(20); // "I will do bad things with your money"
console.log(secondAccount.withdraw(30)); // "Insufficient money"
console.log(secondAccount.withdraw(20)); // 0
```

### For loop with var before ES6

We could see the following use of IIFE in some old code, before the introduction of the statements **let** and **const**
in **ES6** and the block scope. With the statement **var**, we have only function scopes and the global scope.
Suppose we want to create 2 buttons with the texts Button 0 and Button 1 and when we click
them, we would like them to alert 0 and 1. The following code doesn't work:

```js
for (var i = 0; i < 2; i++) {
const button = document.createElement("button");
button.innerText = `Button ${i}`;
button.onclick = function () {
console.log(i);
};
document.body.appendChild(button);
}
console.log(i); // 2
```

When clicked, both Button 0 and Button 1 alert 2 because `i` is global,
with the last value 2. To fix this problem before ES6, we could use the IIFE pattern:

```js
for (var i = 0; i < 2; i++) {
const button = document.createElement("button");
button.innerText = `Button ${i}`;
button.onclick = (function (copyOfI) {
return function () {
console.log(copyOfI);
};
})(i);
document.body.appendChild(button);
}
console.log(i); // 2
```

When clicked, Buttons 0 and 1 alert 0 and 1.
The variable `i` is globally defined.
Using the statement **let**, we could simply do:

```js
for (let i = 0; i < 2; i++) {
const button = document.createElement("button");
button.innerText = `Button ${i}`;
button.onclick = function () {
console.log(i);
};
document.body.appendChild(button);
}
console.log(i); // Uncaught ReferenceError: i is not defined.
```

When clicked, these buttons alert 0 and 1.
For code examples, see the [`function` expression](/en-US/docs/Web/JavaScript/Reference/Operators/function) and [`async function` expression](/en-US/docs/Web/JavaScript/Reference/Operators/async_function) reference pages.

## See also

- [IIFE](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression) (Wikipedia)
- [Comma operator](/en-US/docs/Web/JavaScript/Reference/Operators/Comma_operator)
- Related glossary terms:
- {{Glossary("Function")}}
- {{Glossary("Self-Executing Anonymous Function")}}
11 changes: 0 additions & 11 deletions files/en-us/glossary/self-executing_anonymous_function/index.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,23 @@ add(10).then((v) => {
});
```

### Async IIFE

An `async` [IIFE](/en-US/docs/Glossary/IIFE) allows you to use [`await`](/en-US/docs/Web/JavaScript/Reference/Operators/await) and [`for...await`](/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of) in contexts where [top-level await](/en-US/docs/Web/JavaScript/Reference/Operators/await#top_level_await) is not available. Here we use an [arrow function](/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) to define the IIFE, but `async function` expressions can also be used.

```js
const getFileStream = async (url) => {
// implementation
};

(async () => {
const stream = await getFileStream("https://domain.name/path/file.ext");
for await (const chunk of stream) {
console.log({ chunk });
}
})();
```

## Specifications

{{Specifications}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ expr1, expr2, expr3/* , … */

## Description

You can use the comma operator when you want to include multiple expressions in a location that requires a single expression. The most common usage of this operator is to supply multiple updaters in a `for` loop.
You can use the comma operator when you want to include multiple expressions in a location that requires a single expression. The most common usage of this operator is to supply multiple updaters in a `for` loop. For an idiom allowing multiple _statements_ in a location that requires a single expression, you may use an [IIFE](/en-US/docs/Glossary/IIFE).

Because all expressions except the last are evaluated and then discarded, these expressions must have side effects to be useful. Common expressions that have side effects are assignments, function calls, and [`++`](/en-US/docs/Web/JavaScript/Reference/Operators/Increment) and [`--`](/en-US/docs/Web/JavaScript/Reference/Operators/Decrement) operators. Others may also have side effects if they invoke [getters](/en-US/docs/Web/JavaScript/Reference/Functions/get) or trigger [type coercions](/en-US/docs/Web/JavaScript/Data_structures#type_coercion).

Expand Down Expand Up @@ -148,3 +148,4 @@ globalThis.isDirectEval = false;
## See also

- [`for`](/en-US/docs/Web/JavaScript/Reference/Statements/for)
- [IIFE](/en-US/docs/Glossary/IIFE)
Loading
Loading