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 1 commit
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
203 changes: 10 additions & 193 deletions files/en-us/glossary/iife/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,14 @@ page-type: glossary-definition

{{GlossarySidebar}}

An **IIFE** (Immediately Invoked Function Expression) is an idiom in which a {{glossary("JavaScript")}} {{glossary("function")}} runs as soon as it is defined.
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…
})();

// IIFE with arguments
(function (a, b) {
console.log(a + b);
return a * b;
})(1, 2); // logs 3. evaluates to 2.

// arrow function variant
(() => {
// statements…
Expand All @@ -30,202 +23,26 @@ The name IIFE is promoted by Ben Alman in [his blog](https://web.archive.org/web
(async () => {
// statements…
})();

// IIFE being used to initialize a variable
const value = (() => {
const randomValue = Math.random();
if (randomValue > 0.5) {
return "heads";
} else {
return "tails";
}
}());
```

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. 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.

2. Immediately _calling_ the function expression. Arguments may be provided,
though IIFEs without arguments are more common.

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.

## Use cases

### Avoid polluting the global namespace in script code

Because our application could include many functions and global variables from different source files, it's
important to limit the number of variables at the top level of a script
(this advice [does not apply to module code](/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_classic_scripts)).
If we have some initialization code that we don't need to use
again, we could use the IIFE pattern. As we will not reuse the code again, using an IIFE in this case is better than
using a function declaration or a function expression.

```js
// top-level of a script (not a module)

var globalVariable = (() => {
// some initialization code
let firstVariable = something();
let secondVariable = somethingElse();
return firstVariable + secondVariable;
})();

// firstVariable and secondVariable cannot be accessed outside of the function body.
```

### 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.
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.

### Control flow statements in expression positions
Use cases of IIFEs include:

IIFEs enable us to use language constructs such as `switch` in an 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
someObject.property = (() => {
switch (someVariable) {
case 0:
return "zero";
case 1:
return "one";
default:
return "unknown";
}
})();
```

This approach can be especially useful in scenarios where you want to make a variable `const`, but
are forced to use `let` or `var` during initialization:

```js
let onlyAssignedOnce;
try {
onlyAssignedOnce = someFunctionThatMightThrow();
} catch (e) {
onlyAssignedOnce = null;
}
```

Using IIFEs, we can make the variable `const`:

```js
const onlyAssignedOnce = (() => {
try {
return someFunctionThatMightThrow();
} catch (e) {
return null;
}
})();
```
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)
- [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
Loading
Loading