A simple and clear guide to basic functions, function expressions, anonymous functions, IIFEs, and more in JavaScript

Simon Tharby
May 25 · 13 min read

Whilst delving into the more nuanced forms of JavaScript functions, I found myself using various syntactic structures without fully understanding them. For example:

An anonymous Immediately-invoked Function Expression (IIFE).

My main question when looking at the perfectly valid—but somewhat cryptic—code above was, ‘Why so many parentheses?’ In answering this question through experimentation and research, other more basic questions appeared which, in turn, required a review of some fundamental concepts of functions in JavaScript. This post describes and explores:

Basic functions

Any intermediate user of JavaScript is familiar with the basic structure and usage of a function. For example:

‘// ->’ indicates console output

A function named add is declared, which accepts two values for the parameters x and y, and returns the sum of these two values. A variable named mySum is then declared, and its value set to the value returned when the function is called with the integer values of 4 and 5 passed as arguments to the function. Lastly, the value of mySum is output to the console: 9.

This is basic stuff. There is some subtlety to be found within this structure, however. Let us consider this even simpler function:

The empty parentheses (), used since we are not using parameters in this function, are not optional (as they are in some other languages).

Failing to include the () in the function itself will throw the error: ‘SyntaxError: Unexpected token { …’

Failing to include the () in the function call, however, will simply fail silently, or more accurately, it will appear to do nothing. This is our first clue to a somewhat hidden quality to our basic function declaration. Note how this behavior contrasts with simply including an undefined name in our code:

We define a function and name it basicFunc. More precisely, we create a variable, named basicFunc, and set its value to the function we define. We can see this is the case by pointing our ‘function name’ to a new value:

‘Redefining’ a function as a string!

We set the new value without the need to declare the variable with let, var or const — indeed, this would throw an error, since this initial declaration already occurred ‘behind the scenes’ when we defined the function. A call to the function then fails after we point the identifier to a string, as the name no longer refers to a function.

Function expressions

In fact, our basic function declaration and usage is equivalent to:

Here, we declared a variable named funcExp and explicitly set its value to an anonymous function. This is a form of a function expression. It behaves exactly like our previous, less explicit version, including the fact that the value of the declared variable can be changed. Our earlier version is, therefore, also naming a variable which is then ‘pointed at’ an anonymous function (even though it was not written as such an expression).

When writing your own code, where you are clear about the names you have used, the chance of accidentally ‘overwriting’ a function may seem negligible. However, when incorporating code from other sources (libraries, modules, etc.) or attempting to understand or refactor other people’s work, such issues may be significant.

Fortunately, we can prevent redefinition of our function by declaring it with const (not let or var). For example:

Using a ‘const’ function expression prevents redefinition.

Now our function will always be the function we first declare it as, and any attempt to redefine or alter it, accidental or otherwise, will throw an error.

I had thought this syntax was simply an older, but effectively equivalent, form of function declaration, but this slightly more verbose form enables greater control and provides more information to a reader of our code.

Anonymous functions

Function expressions (see above), as noted, relate an anonymous function to a named variable. In fact, all forms of functions other than the first basic form of a function considered here (see ‘Basic functions’, above), explicitly involve an anonymous function form.

Some commentators will argue that any such use of anonymous functions makes tracing errors to line numbers and/or function names difficult or impossible. This seems to be an overstatement of the potential error reporting difficulties of the wholly anonymous function and, more significantly, a conflation of the use of such functions with use of the form of an anonymous function within a named function (a function related to an identifier). To demonstrate this point, let’s look at three different forms of function, each containing the same error, log their types, and compare the stack traces (Spoiler: they are identical):

The stack traces for the errors in the three forms above are effectively identical, including references to the exact point of the error (including line number(s)), and to the function ‘name’.

Moreover, despite using the form of an anonymous function within the 2nd and 3rd examples, the resulting type is a named function in all cases. Thus, we see no difference in the interpreter’s ability to trace the error and name the function containing it.

There are some cases (see ‘Varieties of IIFEs’, below) where a function expression is never assigned an identifier (not named in any sense), and thus remains a truly anonymous function in totality. Obviously, in such cases, our stack trace could never refer to a function ‘name,’ but I remain unaware of any difficulty in tracing errors within such constructs — if you do know of any such issues, please let me know in the comments.

Arrow functions

The third form of function containing an error (above) is an arrow function. If we recreate our basic function as an arrow function, we get:

There are two main potential benefits of using this structure:

1: Shorter syntax

By omitting the function keyword, we reduce the overall character count (but perhaps reduce the transparency of the code somewhat). In special cases, we can reduce the character count further — for example, we can omit the parentheses entirely when only one parameter is used:

The astute will notice that this example also omits the curly braces. Where the function contains only one expression, which represents the value to be returned (or only a single statement to be executed), curly braces are also optional.

2: No binding of the ‘this’ keyword

Arrow functions inherit their scope from the parent scope, which is called “lexical scoping.” Whilst lexical scoping is guaranteed when using arrow functions (and thus the value of the this keyword will always be derived from the parent scope in which it is defined), other forms of functions have a scope dependent on their execution context, and thus their value of this also varies according to the execution context.

If the previous paragraph just reads like a word salad, it might indicate a need to better understand the concept of scope in JavaScript (try this post by Cristian Salcescu) and/or develop a reasonable grasp of the somewhat more complex this keyword (I recommend this post by Brandon Morelli).

Sometimes, code speaks more clearly than prose. The following examples (run in a browser console) demonstrate the lexical scoping of arrow functions, as against the contextual scoping of other function forms:

Above, an arrow function is defined within the global (window) scope. The value of this for this function is window whether it is called from the global scope or from within an object.

In contrast, the above example is not an arrow function, and its value of this is no longer window when it is called from within an object (since it was executed within the scope of the object).

For one example of the utility of the lexical scoping of arrow functions, see the following section: ‘Factory functions.’

There are two main potential drawbacks of using arrow functions:

1: More opaque syntax:

Personally, I like the sparser syntax of the arrow function. Once learned, it is generally easy to parse in real-world code bases. However, the omission of the function keyword is admittedly not very beginner-friendly.

Certainly, when combined with other sparse forms (e.g. some forms of immediately-invoked function expressions, as presented in this post’s introduction and in the respective section below), such terse structures can become cryptic to a mind-bending extent for the uninitiated.

2. Lexical scoping prevents manual binding of scope:

As mentioned above, full understanding of scope, its binding, and resultant effects on the value of the keyword this, takes some effort to arrive at (I am still working on getting there). I recommend this post by Dario Garcia Moya as a good introduction to the issues related to arrow functions.

Basically, and oversimplifying to avoid a list of specific exceptions, if any of your code uses call, apply or bind to specify a scope for some action(s), then avoid using arrow functions in the context of that code. If you are not using those methods to bind a particular scope (and you likely aren’t if you don’t yet understand what that means), then consider yourself cleared to use arrow functions — probably. I remain equivocal, since the caveats (some more of which are revealed in the following section) can seem relatively subtle.

Factory functions:

“A factory function is any function which is not a class or constructor that returns a (presumably new) object. In JavaScript, any function can return an object. When it does so without the new keyword, it’s a factory function.” (Eric Elliot, JavaScript Factory Functions with ES6+ )

The lexical scoping of arrow functions, described in the previous section, makes them useful in many cases for providing a simpler, less verbose way to create object instances. Let’s first review the constructor pattern for creating object instances:

Note how this contrasts with a factory function approach:

The lexical scoping of the arrow function has removed the need to use the this keyword repeatedly and, therefore, greatly simplified the structure. Note also that the new keyword is not required to instantiate the new instance.

Although the behaviors are identical in the two limited examples above, the forms are not equivalent in all respects. One major difference is that a factory function produces a singleton object, whereas the constructor pattern will inherit from the object prototype (where various attributes and methods can be defined).

The choice to use one pattern over the other may be complex, and/or one based on style/taste/best fit with the overarching pattern(s). Sometimes the choice might be simple; for example, if the number of (singleton) object instances produced by a factory function becomes large enough that it will substantially impact memory usage, then the constructor pattern may enable a significant reduction in memory usage by moving a portion of the object instances’ functionality to the object prototype. Conversely, if the objects’ functionalities diverge significantly from one another, it may not be possible to simply transfer such functionality to a prototype. Furthermore, there are cases where a singleton is used to ensure immutability and/or avoid the instantiation of more than one such object.

I know this is an area I am not yet qualified to expound upon at length, and I will, therefore, recommend further reading, as much for myself as for you, the reader. We might do well to start with a careful reading of Eric Elliot’s post, quoted at the beginning of this section, and then continue with An introduction to the JavaScript constructor pattern, by Chris Ferdinandi.

Immediately-invoked function expressions: The basics

Let us now turn to functions that execute as soon as they are encountered at run-time — immediately-invoked function expressions (IIFEs) — a.k.a. ‘iffys’. Here is a very simple example of an anonymous IIFE:

There is no separate statement to call the function, as the final () does this. The void keyword indicates that what follows is an expression, not a function definition. We can replace void with the more cryptic ! for the same effect:

The latter form could be considered more likely to confuse the uninformed, since in JavaScript (and many other languages), the exclamation mark !, a.k.a ‘bang,’ is the logical ‘not’ operator. As a side note, it does, in fact, also operate as such, as well as invoking the function expression:

Thus, the more cryptic ! could be used to intentionally make use of its Boolean operation. It might also be used to help avoid error in the case where a previously concatenated module missed a trailing semicolon. Lastly, it may save a byte or two of memory over other forms.

A third way to mark the function as an expression is to wrap it in parentheses, giving us this form:

And if we make use of the arrow function structure to remove the function keyword, we finally arrive at the structure shown in the introduction to this post that first piqued my interest:

Having now answered my original question of ‘Why so many parentheses?’, we might well ask a more important question: ‘What is the point?’ Since the examples given so far simply execute a statement within each function, there seems little difference to just executing the statement without wrapping it in a function expression.

The main reason for using an IIFE is that a function expression creates a new scope context or ‘closure’ (and I would, once again, recommend this post by Cristian Salcescu if you need a review of the concept of scope). This can be illustrated using a contrived example:

We are able to declare a second variable with an identical identifier, name, which would be illegal within the same scope, and make use of each variable within their respective scopes, without them interfering with the values of each other.

This can have an immediate benefit when writing code that includes many similar, but distinct, operations. For example, when writing a calculator app, I found myself wanting to use variable names like result in many places. By trying to use descriptive identifiers (to improve code readability) I resorted to using variations such as tempResultA, tempResultB, etc. Far from ideal, and easily resolved by judicious use of IIFEs (or other scoping solutions).

This sort of encapsulation avoids ‘polluting’ the global (or parent) scope. It also helps protect the encapsulated code from the accidental / unintended effects of changing global values.

Varieties of IIFEs

Let’s take things a step further by declaring and pointing a variable identifier at an immediately invoked function:

Whilst this does provide us with all the scope related advantages of IIFEs, this specific case does not add any additional functionality. Despite attempting to assign our IIFE an identifier, we have simply assigned a value of ‘undefined’, since the function has executed, yet none of that execution provided a value (it simply logged a string to the console). We have, therefore, placed an identifier in memory to no benefit.

However, if we return a value from our function expression, rather than simply executing statements, we will successfully set our variable to that value:

Note that this form does not need the parentheses encapsulating the function expression when run in a terminal, but will throw an error in certain browsers without them. The arrow function form below does require such encapsulating parentheses in all cases (and will throw an error without them):

This pattern provides us with a neat way of reaping the benefits of scope encapsulation, and the closure this creates, when setting (or computing) values of variables.

This is a good point at which to remind ourselves that we can also pass arguments to the parameters of IIFEs, just as with regular functions. For example:

Interesting things happen if we add curly braces around our return statement:

These curly braces tell JavaScript to return an object literal, which means we can read the value associated with the ary key, in our example above, using arrowIIFE.ary, which represents the form: <IIFE name>.<object literal key>

We can also add a function to alter our array, and return that function as an object literal too, providing us with the ability to change our declared array after the IIFE has executed:

And with this pattern we have arrived at a very real-world example — the module pattern.

This is a pattern I had used, without fully understanding it, when completing an exercise with the aim of creating a tic-tac-toe game. The following example is the board module from my solution to the exercise:

You can probably guess that the above example uses a 9-element array to represent the tic-tac-toe board. Hopefully, if you have followed this post to this point, you also understand how the addPiece and reset functions would be used within the board namespace.

Thus far, we have seen IIFEs that simply run immediately, and IIFEs that also set the value of a variable. You might also be wondering, ‘Can a named IIFE be created that can be called again after its immediate invocation?’ Well, yes it can:

Here, we see an IIFE that contains a named function definition which returns itself. Thus it executes the code within the named function on initial execution and every subsequent time the IIFE is called. In this example, we also have a parameter, msg, with a default argument included in the IIFE.

IIFEs in the wild

Before finishing, let’s spend a little time on how and why IIFEs are really used.

Data privacy:

Example from Use Cases for JavaScript’s IIFEs, by Marius Schultz;

The count variable, and the function that increments the variable and uses the variable’s values in the id strings it returns, are both inaccessible from outside of the IIFE, due to its creation of a closure (a local scope). Thus, the process of unique id production is protected from accidental or intentional modification (except by directly rewriting the IIFE).

Module pattern as API:

Example from Learning JavaScript Design Patterns, by Addi Osmani:

The module pattern used here not only neatly namespaces its methods inside basketModule, but also presents a number of methods via the publicly returned API whilst keeping a number of methods private.

Better timed repetition:

This example is adapted from JavaScript Self Invoking Functions, by Safraz Ahmed.

Suppose you want your webpage to run some code repetitively every 5 seconds, perhaps to query a database or API, or for some other reason. The simple (but less effective) approach is to use setInterval to call a function every 5 seconds:

However, if the code in doStuff does not finish in 5 seconds it will be called again regardless. It is not hard to imagine (or experience) how this may not give the expected results, and could even lead to serious issues. If we use an IIFE that calls itself only after a set time has elapsed following the code execution, such problems are avoided:

I have only scratched the surface of real-life usage of IIFEs, but hopefully you are now well armed to identify and better understand them as and when you encounter them.

Conclusion:

Various forms of functions are available to the JavaScript programmer, many of which may leverage different aspects of underlying concepts such as closure, scope, binding, time and mode of invocation, and more.

There is much control and efficacy to be had, many errors to be avoided, and a deeper understanding of the real-world code-base to be gained, through improved proficiency with JavaScript functions.

Resources used:

An introduction to the JavaScript constructor pattern, by Chris Ferdinandi

An introduction to scope in JavaScript, by Cristian Salcescu

Essential JavaScript: Mastering immediately-invoked functions expressions, by Chandra Gundamaraju

JavaScript: Arrow functions for beginners, by Brandon Morelli

JavaScript Design Patterns: The singleton, by Samier Saeed

JavaScript Factory Functions with ES6+, by Eric Elliot

JavaScript Self Invoking Functions, by Safraz Ahmed

JavaScript: The keyword ‘this’ for beginners, by Brandon Morelli

Learning JavaScript Design Patterns, by Addi Osmani

Understanding ‘this’ with JavaScript in arrow functions, by Dario Garcia Moya

Use Cases for JavaScript’s IIFEs, by Marius Schultz

What do empty parentheses … ?, StackOverflow

What does the exclamation mark … ?, StackOverflow

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade