What are the different ways of writing an IIFE? What are their use cases?
Asked Answered
D

2

6

I have started reading this book. Chapter 2 says that there are different ways to write an IIFE:

!function (){}() 
~function (){}() 
+function (){}() 
-function (){}()
new function (){} 
1,function (){}() 
1&&function (){}() 
var i=function (){}()

The author says:

Each manifestation has its own unique qualities and advantages—some with fewer bytes, some safer for concatenation, each valid and each executable.

I'm a newbie to JS. I know what an IIFE is, but what do these IIFE forms do exactly?

Dyan answered 11/11, 2015 at 13:7 Comment(7)
They missed my favourite, (function() {})()Countermeasure
virtually the same thing @JamesThorpe and it's twin without the dogs bollock, (function() {}())Seedbed
These functions do absolutely nothing:-), see this blog for more information: benalman.com/news/2010/11/…Nonflammable
And void function() {}()Equisetum
@joews: Oh, wow, I've never seen it used, but you're 100% right of course.Prudential
@joews: void in js?Dyan
@Dyan void <expression>: "evaluate <expression> and return undefined" - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Equisetum
P
10

Why do this?

Before we get to the list, let's start with "Why do this at all?"

The answer is: To keep any variables and function declarations within the function private. Commonly this is to avoid globals (avoiding globals is a Good IdeaTM). E.g.:

+function() {
    function foo() {
        /* ... */
    }

    foo();

    var answer = 42;
}();

Thanks to the IIFE (called a scoping function in this context), foo and answer are not globals. They're private to the code within the function, unless they get exported somehow.

You might do this even if not at global scope, just to avoid polluting whatever scope you're in.

IIFEs in general have other uses, but the style you've quoted is typically used for scoping.

The examples

The author is dramatically overstating the case that "each has its own unique qualities and advantages".

Unless you're using the return value, these are all exactly the same:

!function (){}() 
~function (){}() 
+function (){}() 
-function (){}()
1,function (){}() 
1&&function (){}() 

The code within them is run, scoped within the function.

We can add these to that list as well:

(function(){}())
(function(){})()
0||function (){}() 
1^function(){}() // any binary math operator in place of ^ also works

Of course, the 1 in all of the above is not special. Could be any number (or just about anything else) for most of them, but the one using && wouldn't work with 0, "", null, undefined, NaN, or false (the function wouldn't get run). Similarly, the one with 0||... works as long as the value starting it is falsey.

In this one:

var i=function (){}()

...the only difference is that it declares a variable, i, which stores the return value. That can, of course, be a big difference. Consider this more obvious version of it:

var MyPseudoNamespace = function() {
    // ...

    return { /* nifty "namespace" stuff here */ };
})();

Finally:

new function (){} 

That creates a new object and then calls the function with this set to the new object. If you don't use this within the function, it's utterly pointless. If you do, well, whether it's useful depends on what you do with this.


Note: If there's any possibility of code you don't control coming immediately before your scoping function (when you're combining and minifying files, for instance), it's best to start all of these off with a ;, e.g.:

;!function (){}() 
;~function (){}() 
;+function (){}() 
;-function (){}()
;1,function (){}() 
;1&&function (){}() 
;(function(){}())
;(function(){})()
;0||function (){}() 
;1^function(){}() // any binary math operator in place of ^ also works

Several of them don't technically need one, but most of them do. The side-effects of not having them can be subtle, or catastrophic. Consider:

Code before your code:

obj.prop = function() {
    // Do something big and awful
}

Then your code:

(function(){}())

Automatic Semicolon Insertion will not kick in! The result? the obj.prop function gets called, with our IIFE passed into it as an argument. This will make it more obvious:

obj.prop = function() {
    // Do something big and awful
}(function(){}())

See how those () are now invoking the function?

Similarly:

obj.criticalValue = 42

then

+function(){}()

Suddenly, criticalValue is messed up. Why? Because:

obj.criticalValue = 42+function(){}()

Doh!

Having multiple ; in a row is harmless, so if you start off with one, you're less likely to run into trouble.

Prudential answered 11/11, 2015 at 13:12 Comment(5)
The (function() {}) form can lead to unexpected invocation if preceded by a non-II function expression with no semicolon: var f = function(x) { console.log ("called with x: " + x) } /* new line, no semi-colon */ (function() { console.log("second") }())Equisetum
Sounds like a bizarre edge case but I've seen it happen in the wild. Moral: always use semicolons if you favour parethesis-style IIFEs (or just "always use semicolons").Equisetum
@joews: Many of these can lead to unexpected outcomes with improperly terminated code in front of them, not just the (function...) ones. A leading semicolon is a good defense, that's what I always do with minifying, for example.Prudential
@joews: Thanks to your excellent note, I've added an entire thing about that to the end.Prudential
Ah, of course! I hadn't given the operator prefixes much thought before now.Equisetum
M
0

It is an optimization to concatenate multiple files into one. The idea there is, if the last line of the previous file does not end with a newline, then the first line in this file starts in the middle of a line.

Adding things like ; before the function permits it to still work correctly.

Malposition answered 11/11, 2015 at 13:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.