Immediately invoked function expression: Difference between revisions
Fixed typo I made. Tags: Mobile edit Mobile web edit |
Citation bot (talk | contribs) Add: date, authors 1-1. Removed parameters. Some additions/deletions were parameter name changes. | Use this bot. Report bugs. | Suggested by Superegz | Category:Programming language concepts | #UCB_Category 12/23 |
||
(42 intermediate revisions by 31 users not shown) | |||
Line 1: | Line 1: | ||
{{Short description|Javascript design pattern}} |
|||
An '''immediately invoked function expression''' (or '''IIFE''', pronounced "iffy", [[International Phonetic Alphabet|IPA]] /ˈɪf.i/) is a [[Programming idiom|programming language idiom]] which produces a [[scope (computer science)|lexical scope]] using [[function scoping]]. It was popular in [[JavaScript]]<ref name="Alman">{{cite web|url=http://benalman.com/news/2010/11/immediately-invoked-function-expression/|title=Immediately Invoked Function Expressions|last=Alman|first=Ben|date=15 November 2010|website=|url-status=live|archive-url=https://web.archive.org/web/20171201033208/http://benalman.com/news/2010/11/immediately-invoked-function-expression/|archive-date=1 December 2017|accessdate=18 January 2019}}</ref> as a method to support [[modular programming]] before the introduction of more standardized solutions such as [[CommonJS]] and [[ECMAScript#6th Edition – ECMAScript 2015|ES modules]].<ref>{{cite web |last1=McGinnis |first1=Tyler |title=JavaScript Modules: From IIFEs to CommonJS to ES6 Modules |url=https://ui.dev/javascript-modules-iifes-commonjs-esmodules/ |website=ui.dev |access-date=18 August 2021 |language=en |date=15 January 2019}}</ref> |
|||
An '''immediately-invoked function expression''' (or '''IIFE''', pronounced "iffy"<ref name=Alman>{{cite web|last=Alman|first=Ben|title=Immediately Invoked Function Expressions|url=http://benalman.com/news/2010/11/immediately-invoked-function-expression|year=2010|accessdate=4 February 2013}}</ref>) is a [[JavaScript]] [[Programming_idiom|programming language idiom]] which produces a [[scope (computer science)|lexical scope]] using JavaScript's [[function scoping]]. Immediately-invoked function expressions can be used to avoid [[JavaScript syntax#Scoping_and_hoisting|variable hoisting]] from within blocks, protect against polluting the [[Global variable|global environment]] and simultaneously allow public access to methods while retaining privacy for variables defined within the function. This concept has been referred to as a '''self-executing anonymous function''',<ref>{{cite book|last=Resig|first=John|title=Pro JavaScript Techniques|year=2006|publisher=Apress|isbn=9781430202837|pages=29}}</ref> but Ben Alman introduced the term IIFE as a more semantically accurate term for the idiom, shortly after its discussion arose on comp.lang.javascript.<ref name=Alman /><ref name=Osmani>{{cite book|last=Osmani|first=Addy|title=Learning JavaScript Design Patterns|year=2012|publisher=O'Reilly|isbn=9781449334871|pages=206}}</ref> |
|||
<ref>{{cite news|last1=Baagoe|first1=Johannes|title=Closing parenthesis in function's definition followed by its call|url=https://groups.google.com/forum/#!topic/comp.lang.javascript/tjVn1NjGDN8%5B1-25%5D|accessdate=19 April 2010}}</ref> |
|||
Immediately invoked function expressions can be used to avoid [[JavaScript syntax#Scoping and hoisting|variable hoisting]] from within blocks, protect against polluting the [[Global variable|global environment]] and simultaneously allow public access to methods while retaining privacy for variables defined within the function. |
|||
⚫ | |||
⚫ | Immediately |
||
⚫ | |||
⚫ | Immediately invoked function expressions may be written in a number of different ways.<ref name=Enlighten>{{cite book |last=Lindley |first=Cody |title=JavaScript Enlightenment |year=2013 |publisher=O'Reilly |isbn=978-1-4493-4288-3 |page=61}}</ref> A [[Coding conventions|common convention]] is to enclose the function expression{{spnd}}and optionally its invocation operator{{spnd}}with the grouping operator,<ref>{{cite web |url=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Grouping |title=Grouping operator |date=2 October 2023 |publisher=Mozilla Developer Network}}</ref> in parentheses, to tell the parser explicitly to expect an expression. Otherwise, in most situations, when the parser encounters the <code>function</code> keyword, it treats it as a function declaration (statement), and not as a function expression.<ref>{{cite book |last=Zakas |first=Nicholas |title=Maintainable JavaScript |year=2012 |publisher=O'Reilly |isbn=978-1-4493-2768-2 |page=44}}</ref><ref>{{cite web |url=http://exploringjs.com/es6/ch_arrow-functions.html#iiaf |title=ExploringJS |author=Axel Rauschmayer}}</ref> |
||
<syntaxhighlight lang="JavaScript"> |
<syntaxhighlight lang="JavaScript"> |
||
(function () { |
(function () { /* ... */ })(); |
||
(function () { |
(function () { /* ... */ }()); |
||
(() => { /* ... */ })(); // With ES6 arrow functions (though parentheses only allowed on outside) |
|||
</syntaxhighlight> |
</syntaxhighlight> |
||
There are other ways to enforce a function expression: |
There are other ways to enforce a function expression:{{cn|date=September 2022}} |
||
<syntaxhighlight lang="JavaScript"> |
<syntaxhighlight lang="JavaScript"> |
||
!function () { |
!function () { /* ... */ }(); |
||
~function () { |
~function () { /* ... */ }(); |
||
-function () { |
-function () { /* ... */ }(); |
||
+function () { |
+function () { /* ... */ }(); |
||
void function () { /* ... */ }(); |
|||
delete function () { /* ... */ }(); |
|||
typeof function () { /* ... */ }(); |
|||
await function () { /* ... */ }(); |
|||
</syntaxhighlight> |
</syntaxhighlight> |
||
In contexts where an expression is expected, wrapping in parentheses is not necessary: |
In contexts where an expression is expected, wrapping in parentheses is not necessary: |
||
<syntaxhighlight lang=" |
<syntaxhighlight lang="javascript"> |
||
let f = function () { /* ... */ }(); |
|||
true && function () { |
true && function () { /* ... */ }(); |
||
0, function () { |
0, function () { /* ... */ }(); |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
Passing variables into the scope is done as follows: |
Passing variables into the scope is done as follows: |
||
<syntaxhighlight lang="JavaScript"> |
<syntaxhighlight lang="JavaScript"> |
||
(function(a, b) { |
(function(a, b) { /* ... */ })("hello", "world"); |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
An initial parenthesis is one case where the |
An initial parenthesis is one case where the [[automatic semicolon insertion]] (ASI) in JavaScript can cause problems; the expression is instead interpreted as a call to the last term on the preceding line. In some styles that omit optional semicolons, the semicolon is placed ''in front'' of the parenthesis, and is known as a [[defensive semicolon]].<ref name=inimino>{{cite web |url=http://inimino.org/~inimino/blog/javascript_semicolons |title=JavaScript Semicolon Insertion: Everything you need to know |date=28 May 2010 |archive-url=https://web.archive.org/web/20171002224530/http://inimino.org/~inimino/blog/javascript_semicolons |archive-date=2 October 2017 |url-status=live}}</ref><ref>{{cite web |url=https://mislav.net/2010/05/semicolons/ |title=Semicolons in JavaScript are optional |first=Mislav |last=Marohnić |date=7 May 2010 |archive-url=https://web.archive.org/web/20170808231150/https://mislav.net/2010/05/semicolons/ |archive-date=8 August 2017 |url-status=live}}</ref> For example: |
||
<syntaxhighlight lang="JavaScript"> |
<syntaxhighlight lang="JavaScript"> |
||
a = b + c |
a = b + c |
||
Line 37: | Line 44: | ||
})(); |
})(); |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
...to avoid being parsed as <code>c()</code>. |
|||
==Examples== |
==Examples== |
||
The key to understanding design patterns such as |
The key to understanding design patterns such as IIFE is to realize that prior to ES6, JavaScript only featured [[Scope (computer science)#Function scope|function scope]] (thus lacking [[Scope (computer science)#Block scope|block scope]]), passing [[Call_by_reference|values by reference]] inside [[Closure (computer science)|closure]]s.<ref>{{cite book |last=Haverbeke |first=Marijn |title=Eloquent JavaScript |year=2011 |publisher=No Starch Press |isbn=978-1-59327-282-1 |pages=29–30}}</ref> This is no longer the case, as the ES6 version of JavaScript implements block scoping using the new <code>let</code> and <code>const</code> keywords.<ref>ECMAScript 6: New Features: Overview & Comparison, [http://es6-features.org/#BlockScopedVariables Block-Scoped Variables]</ref> |
||
===Evaluation context=== |
=== Evaluation context === |
||
A lack of block scope means that variables defined inside |
A lack of block scope means that variables defined inside (for example) a [[for loop]] will have their definition "hoisted" to the top of the enclosing function. Evaluating a function that depends on variables modified by the outer function (including by iteration) can be difficult. We can see this without a loop if we update a value between defining and invoking the function.<ref>{{cite web |last=Alman |first=Ben |title=simple-iife-example.js |url=https://gist.github.com/cowboy/4710214 |work=Github |accessdate=5 February 2013}}</ref> |
||
<syntaxhighlight lang=" |
<syntaxhighlight lang="javascript"> |
||
let v, getValue; |
|||
v = 1; |
v = 1; |
||
getValue = function () { return v; }; |
getValue = function () { return v; }; |
||
v = 2; |
v = 2; |
||
getValue(); // 2 |
getValue(); // 2 |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
⚫ | |||
⚫ | |||
⚫ | Hereafter the function passes <code>v</code> as an argument and is invoked immediately, preserving the inner function's execution context.<ref name=JQ>{{cite book| |
||
⚫ | Hereafter the function passes <code>v</code> as an argument and is invoked immediately, preserving the inner function's execution context.<ref name=JQ>{{cite book |last1=Otero |first1=Cesar |last2=Larsen |first2=Rob |title=Professional jQuery |year=2012 |publisher=John Wiley & Sons |isbn=978-1-118-22211-9 |page=31}}</ref> |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
v = 1; |
v = 1; |
||
getValue = (function (x) { |
getValue = (function (x) { |
||
Line 69: | Line 76: | ||
This is equivalent to the following code: |
This is equivalent to the following code: |
||
<syntaxhighlight lang=" |
<syntaxhighlight lang="javascript"> |
||
let v, getValue; |
|||
v = 1; |
v = 1; |
||
function f(x) { |
function f(x) { |
||
Line 81: | Line 88: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
⚫ | |||
David Herman's ''Effective JavaScript'' contains an example illustrating the problems of evaluation context inside loops.<ref>{{cite book|last=Herman|first=David|title=Effective Javascript|year=2012|publisher=Addison-Wesley|isbn=9780321812186|pages=44–45}}</ref> While Herman's example is deliberately convoluted it arises directly from the same lack of block scope.<ref>{{cite book|last=Zakas|first=Nicholas C.|title=Professional JavaScript for Web Developers|chapter=Mimicking Block Scope|year=2011|publisher=John Wiley & Sons|isbn=9781118233092}}</ref> |
|||
⚫ | IIFEs are also useful for establishing private methods for accessible functions while still exposing some properties for later use.<ref>{{cite book |last=Rettig |first=Pascal |title=Professional HTML5 Mobile Game Development |year=2012 |publisher=John Wiley & Sons |isbn=978-1-118-30133-3 |page=145}}</ref> The following example comes from Alman's post on IIFEs.<ref name=Alman/> |
||
⚫ | |||
⚫ | |||
⚫ | IIFEs are also useful for establishing private methods for accessible functions while still exposing some properties for later use.<ref>{{cite book|last=Rettig|first=Pascal|title=Professional HTML5 Mobile Game Development|year=2012|publisher=John Wiley & Sons|isbn= |
||
⚫ | |||
// "counter" is a function that returns an object with properties, which in this case are functions. |
// "counter" is a function that returns an object with properties, which in this case are functions. |
||
let counter = (function () { |
|||
let i = 0; |
|||
return { |
return { |
||
Line 105: | Line 110: | ||
// These calls access the function properties returned by "counter". |
// These calls access the function properties returned by "counter". |
||
counter.get(); // 0 |
counter.get(); // 0 |
||
counter.set(3); |
counter.set(3); |
||
counter.increment(); |
counter.increment(); // 4 |
||
counter.increment(); |
counter.increment(); // 5 |
||
</syntaxhighlight> |
</syntaxhighlight> |
||
If we attempt to access <code>counter.i</code> from the global environment, it will be undefined as it is enclosed within the invoked function and is not a property of <code>counter</code>. Likewise, if we attempt to access <code>i</code> it will result in an error as we have not declared <code>i</code> in the global environment. |
If we attempt to access <code>counter.i</code> from the global environment, it will be undefined, as it is enclosed within the invoked function and is not a property of <code>counter</code>. Likewise, if we attempt to access <code>i</code>, it will result in an error, as we have not declared <code>i</code> in the global environment. |
||
==Terminology== |
== Terminology == |
||
Originally known as a "self-executing anonymous function",<ref>{{cite book |last=Resig |first=John |title=Pro JavaScript Techniques |year=2006 |publisher=Apress |isbn=978-1-4302-0283-7 |page=29}}</ref> Ben Alman later introduced the current term IIFE as a more semantically accurate name for the idiom, shortly after its discussion arose on comp.lang.javascript.<ref name=Alman/><ref name=Osmani>{{cite book |last=Osmani |first=Addy |title=Learning JavaScript Design Patterns |year=2012 |publisher=O'Reilly |isbn=978-1-4493-3487-1 |page=206}}</ref><ref>{{cite news |last=Baagoe |first=Johannes |title=Closing parenthesis in function's definition followed by its call |url=https://groups.google.com/forum/#!topic/comp.lang.javascript/tjVn1NjGDN8%5B1-25%5D |accessdate=19 April 2010}}</ref> |
|||
⚫ | |||
⚫ | Notably, immediately invoked functions need not be anonymous inherently, and [[ECMAScript]]{{nbsp}}5's strict mode forbids <code>arguments.callee</code>,<ref>{{cite web |title=Strict mode |url=https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode#Making_eval_and_arguments_simpler |work=Mozilla JavaScript Reference |publisher=Mozilla Developer Network |accessdate=4 February 2013}}</ref> rendering the original term a [[misnomer]]. |
||
In lambda-calculus, this construct was referred to as "redex", for reducible expression, see [[Reduction strategy (code optimization)]]. |
|||
== |
== See also == |
||
*[[Evaluation strategy]] |
|||
⚫ | |||
== |
== References == |
||
⚫ | |||
⚫ | |||
* ''{{cite web|last=Soshnikov|first=Dmitry|title=ECMA-262-3 in detail. Chapter 5. Functions.|url=http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/#question-about-surrounding-parentheses|accessdate=4 February 2013}}'' |
|||
== External links == |
|||
⚫ | |||
[[Category:JavaScript]] |
[[Category:JavaScript]] |
||
[[Category:Programming language concepts]] |
[[Category:Programming language concepts]] |
Latest revision as of 04:37, 28 April 2024
An immediately invoked function expression (or IIFE, pronounced "iffy", IPA /ˈɪf.i/) is a programming language idiom which produces a lexical scope using function scoping. It was popular in JavaScript[1] as a method to support modular programming before the introduction of more standardized solutions such as CommonJS and ES modules.[2]
Immediately invoked function expressions can be used to avoid variable hoisting from within blocks, protect against polluting the global environment and simultaneously allow public access to methods while retaining privacy for variables defined within the function.
Usage
[edit]Immediately invoked function expressions may be written in a number of different ways.[3] A common convention is to enclose the function expression – and optionally its invocation operator – with the grouping operator,[4] in parentheses, to tell the parser explicitly to expect an expression. Otherwise, in most situations, when the parser encounters the function
keyword, it treats it as a function declaration (statement), and not as a function expression.[5][6]
(function () { /* ... */ })();
(function () { /* ... */ }());
(() => { /* ... */ })(); // With ES6 arrow functions (though parentheses only allowed on outside)
There are other ways to enforce a function expression:[citation needed]
!function () { /* ... */ }();
~function () { /* ... */ }();
-function () { /* ... */ }();
+function () { /* ... */ }();
void function () { /* ... */ }();
delete function () { /* ... */ }();
typeof function () { /* ... */ }();
await function () { /* ... */ }();
In contexts where an expression is expected, wrapping in parentheses is not necessary:
let f = function () { /* ... */ }();
true && function () { /* ... */ }();
0, function () { /* ... */ }();
Passing variables into the scope is done as follows:
(function(a, b) { /* ... */ })("hello", "world");
An initial parenthesis is one case where the automatic semicolon insertion (ASI) in JavaScript can cause problems; the expression is instead interpreted as a call to the last term on the preceding line. In some styles that omit optional semicolons, the semicolon is placed in front of the parenthesis, and is known as a defensive semicolon.[7][8] For example:
a = b + c
;(function () {
// code
})();
...to avoid being parsed as c()
.
Examples
[edit]The key to understanding design patterns such as IIFE is to realize that prior to ES6, JavaScript only featured function scope (thus lacking block scope), passing values by reference inside closures.[9] This is no longer the case, as the ES6 version of JavaScript implements block scoping using the new let
and const
keywords.[10]
Evaluation context
[edit]A lack of block scope means that variables defined inside (for example) a for loop will have their definition "hoisted" to the top of the enclosing function. Evaluating a function that depends on variables modified by the outer function (including by iteration) can be difficult. We can see this without a loop if we update a value between defining and invoking the function.[11]
let v, getValue;
v = 1;
getValue = function () { return v; };
v = 2;
getValue(); // 2
While the result may seem obvious when updating v
manually, it can produce unintended results when getValue()
is defined inside a loop.
Hereafter the function passes v
as an argument and is invoked immediately, preserving the inner function's execution context.[12]
let v, getValue;
v = 1;
getValue = (function (x) {
return function () { return x; };
})(v);
v = 2;
getValue(); // 1
This is equivalent to the following code:
let v, getValue;
v = 1;
function f(x) {
return function () { return x; };
};
getValue = f(v);
v = 2;
getValue(); // 1
Establishing private variables and accessors
[edit]IIFEs are also useful for establishing private methods for accessible functions while still exposing some properties for later use.[13] The following example comes from Alman's post on IIFEs.[1]
// "counter" is a function that returns an object with properties, which in this case are functions.
let counter = (function () {
let i = 0;
return {
get: function () {
return i;
},
set: function (val) {
i = val;
},
increment: function () {
return ++i;
}
};
})();
// These calls access the function properties returned by "counter".
counter.get(); // 0
counter.set(3);
counter.increment(); // 4
counter.increment(); // 5
If we attempt to access counter.i
from the global environment, it will be undefined, as it is enclosed within the invoked function and is not a property of counter
. Likewise, if we attempt to access i
, it will result in an error, as we have not declared i
in the global environment.
Terminology
[edit]Originally known as a "self-executing anonymous function",[14] Ben Alman later introduced the current term IIFE as a more semantically accurate name for the idiom, shortly after its discussion arose on comp.lang.javascript.[1][15][16]
Notably, immediately invoked functions need not be anonymous inherently, and ECMAScript 5's strict mode forbids arguments.callee
,[17] rendering the original term a misnomer.
See also
[edit]References
[edit]- ^ a b c Alman, Ben (15 November 2010). "Immediately Invoked Function Expressions". Archived from the original on 1 December 2017. Retrieved 18 January 2019.
- ^ McGinnis, Tyler (15 January 2019). "JavaScript Modules: From IIFEs to CommonJS to ES6 Modules". ui.dev. Retrieved 18 August 2021.
- ^ Lindley, Cody (2013). JavaScript Enlightenment. O'Reilly. p. 61. ISBN 978-1-4493-4288-3.
- ^ "Grouping operator". Mozilla Developer Network. 2 October 2023.
- ^ Zakas, Nicholas (2012). Maintainable JavaScript. O'Reilly. p. 44. ISBN 978-1-4493-2768-2.
- ^ Axel Rauschmayer. "ExploringJS".
- ^ "JavaScript Semicolon Insertion: Everything you need to know". 28 May 2010. Archived from the original on 2 October 2017.
- ^ Marohnić, Mislav (7 May 2010). "Semicolons in JavaScript are optional". Archived from the original on 8 August 2017.
- ^ Haverbeke, Marijn (2011). Eloquent JavaScript. No Starch Press. pp. 29–30. ISBN 978-1-59327-282-1.
- ^ ECMAScript 6: New Features: Overview & Comparison, Block-Scoped Variables
- ^ Alman, Ben. "simple-iife-example.js". Github. Retrieved 5 February 2013.
- ^ Otero, Cesar; Larsen, Rob (2012). Professional jQuery. John Wiley & Sons. p. 31. ISBN 978-1-118-22211-9.
- ^ Rettig, Pascal (2012). Professional HTML5 Mobile Game Development. John Wiley & Sons. p. 145. ISBN 978-1-118-30133-3.
- ^ Resig, John (2006). Pro JavaScript Techniques. Apress. p. 29. ISBN 978-1-4302-0283-7.
- ^ Osmani, Addy (2012). Learning JavaScript Design Patterns. O'Reilly. p. 206. ISBN 978-1-4493-3487-1.
- ^ Baagoe, Johannes. "Closing parenthesis in function's definition followed by its call". Retrieved 19 April 2010.
- ^ "Strict mode". Mozilla JavaScript Reference. Mozilla Developer Network. Retrieved 4 February 2013.
External links
[edit]- "Functions and function scope". Mozilla JavaScript Reference. Mozilla Developer Network. Retrieved 4 February 2013.