How IIFE cuts link to Global environment?

How IIFE cuts link to Global environment?
0

#1

Hi,

I have been reading some books lately and came across a concept of IIFE, it also is suggested that if we declare a function like below, it creates its own environment and helps prevent accidental change to Global state, but i am a bit confused

  1. How does it cut off linkages to Global environment, it converts it in to expression but the function expression is still in the Global environment ?

2)I see that in almost all libraries they declare function like below , but how does it prevent any changes to global variables ?

Also if i do something like this -


var x = 23;

function test(x) {
  var x = 3;
    console.log(x);
}

I always get the value of x that i want , how can i manipulate the global from here, what is the need for making it an expression …Kindly guide here …

The sample of IIFE


(function(x) {
     return x;
})

#2

All functions have access to global variables, but the global scope doesn’t have access to any variables defined within functions.

In your first example, the function doesn’t access the global variable at all. Instead, it creates a new variable with the same name, which only has local scope. If you hadn’t used the var keyword, you would instead have modified the global variable.

Note that the function doesn’t get called in your example, so nothing is logged to the console.

If you call the function and pass in some data as an argument, it creates a third variable, also called x, but this variable does nothing and is never console logged; again, this is because you’re creating a new variable within the function with var.

Your second example isn’t an IIFE, it’s just a function surrounded with brackets. As the function isn’t assigned to anything, it does nothing.

IIFE syntax is like this:

(function() {
  //do some stuff
})();

#3

@lionel-rowe - Thanks , but i am a bit confused here -

So even if i use IIFE - like below -

var x = 23;

( function () {
	x = 12;
    console.log(x) 
})()

console.log(x) out put will be 12 for global x , so how is this different , how does using IIFE hides and creates a separate environment that is different from a regular function declaration …


#4

Because in that example, you didn’t use the var keyword within the function; the function accesses the global variable x (sets it with x = 12, then prints it with console.log(x)).

That’s why you should always use var (or const or let) for declaring variables.


#5

There are some great explanations of the IIFE syntax in this Stack Overflow thread:

Consider the following:

var a = 'foo';

This means that console.log(a) is equivalent to console.log('foo').

Similarly:

var b = function() {
  console.log('foo');
}

This means b() calls that function. But what if you want to call the function without assigning it to a variable? Simply writing out the function and then appending () causes a syntax error. Instead, you need to surround the function with parentheses so that it can be parsed correctly.

It’s exactly the same as (b)(), which in turn is the same as b().


#6

@lionel-rowe - but the point is that even after using IIFE i can alter the global variables, so there must be some good reason to encapsulate all functions like expressions, if the global cannot access the function scope then why we need to use the parenthesis and create IIFE and why do they say it prevents accidental manipulation of global variables when it clearly can be manipulated …Thanks

Also what is the big real life benefit of IIFE that cannot be achieved with regular functions ?


#7

Think of it like a one-way mirror. The IIFE can still “see” the global scope (and can manipulate it too), but the global scope can’t “see” inside the function.

Say your currency-formatting library contains a helper function called zeroPad(). However, this function isn’t itself part of the API; it’s just used to help formatting the currencies.

With an IIFE, you can declare functions to your heart’s content without worrying about conflicts with other libraries or code (it’s quite likely that other libraries would implement their own zeroPad() functions, which may work differently or take different arguments).


#8

Is it really worth worrying about IIFEs if you aren’t planning on writing a library?


#9

Sometimes you need a part of code you can throw away immediately after using, like a disposable plate - it did it’s job, you finished the meal, but carrying the plate around all the way home would be a waste - so you just trash it. IIFEs are very similar


#10

So i finally found a good use of IIFE , say if i write a function -

function test () {
console.log("TestA");
}

and then some where else i also write -

function test () {
console.log("TestB");
}

there is a strong possibility that TestB will be published as the global environment has been polluted …

How ever if i write –

(function () {
console.log("TestA")
})()

Then the function is not attached to global and also as it has no name it will not pollute it either and affect other similar functions… This can be very useful …

If any one has any other benefits please do mention …

Regards


#11

I’m not convinced your understanding of the global namespace is correct. The global namespace contains variables and functions that can be accessed directly from anywhere in the app. So the strings "TestA" and "TestB" would not exist in the global namespace (they’re logged, not declared as variables). However, the function test() would exist in the global namespace, and that would be a problem because you’ve declared it twice (first as a function that logs "TestA", then as a function that logs "TestB").

The IIFE, meanwhile, executes immediately (unlike the other 2 function declarations, which would have to be called with test()).


#12

Let’s take a step back here and fix some more fundamental misconceptions before talking about IIFEs again. First, there is no way in JavaScript to “cut off linkages” to the global environment. All functions have their own scope that contains variables defined in their blocks as well as a reference to the environment they were created in. If your code tries to access a property that is not defined in a function, JS will go up the chain to the next environment to look there. This will continue until either a value is found or the global object has been checked and nothing found.

In your first example, you’re creating three different variables called x.

var x = 23;  // First variable

function test(x) {  // Second variable
  var x = 3;  // Third variable
  console.log(x);  // "3"
}

If you wrap this in an IIFE, nothing will change.

var x = 23;  // First variable

(function test(x) {  // Second variable, always undefined because it will never be passed anything
  var x = 3;  // Third variable
  console.log(x);  // "3"
})()

Your first variable is never accessed. This is because there’s already a variable x within the function’s scope, so there’s no reason to check outside of it. If you remove all of the x variables in your function, it will access the globally defined x.

var x = 23;  

function test(y) {  
  var z = 3; 
  console.log(x);  // "23"
}

Again, nothing changes if we use an IIFE.

Let’s clear up something else. An IIFE isn’t anything special. It’s just a function. All functions create a closure of variables from the environment they are executed in, but functions cannot access the scope of functions that they’ve called.

function test() {
    var x = "Can you see me?";
    console.log("I'm a test!");
}

test();
console.log(x); // ERROR

(function() {
    var x = "Can you see me?";
    console.log("I'm also a test!")
})();
console.log(x); // ERROR

There’s really no difference between these two (they get called at different times, but it’s not important right now). An IIFE is just another way of writing a function. Since all functions in JavaScript create their own lexical environments, this has often been used to cache values that may change, such as in a for loop.

for(var i = 0; i < users.length; i++) {
    fetch(url)
            .then(response => response.json())
            .then(userData => {
                 var myUser = users[i]; // This will always the be last user in the array
                // Broken app :(
            }
}


for(var i = 0; i < users.length; i++) {
   (function() {
        var storedUser = users[i];
        fetch(url)
            .then(response => response.json())
            .then(userData => {
                 // This still has access to the storedUser variable
                // App works!
            })
    })()
}

We don’t need to do this as much now that we have the let and const keywords, but exploiting closures can still be a useful tool.

The main point that I want to get across is that IIFEs do not keep the global scope out of your functions, they keep your functions out of the global scope. Keep that in mind and re-read @lionel-rowe’s posts. I think that should answer all of your questions.


#13

There is one notable difference between these:

var foo = function() {
  var bar = "Hello World";
  alert(bar);
}
foo();

and

(function() {
  var bar = "Hello World";
  alert(bar);
})();

In the first instance, foo() is in the global namespace and remains callable (i.e. the function becomes a part of the API). However, in the second instance, the function is executed exactly once and can’t be called again elsewhere.

In neither case is bar in the global namespace; it’s only accessible by executing the function.


#14

I think this chapter does a good job of explaining IIFE.


#15

this chapter in the YDKJS series gives a good explanation of why you might need to use anonymous functions


#16

I was going to add one additional point to what @lionel-rowe showed in the last post (2nd example). Even if we gave the IIFE a named function as below:

(function foo() {
  var bar = "Hello World";
  console.log(bar);
})();

foo();

The above stand alone call to foo yield “ReferenceError: namedFunction is not defined”, because even though it is named, it is still isolated from the global namespace.


#17

Re one of the [higher level] reasons why they are important:

So in OO languages (Like Java or C#), variables/methods are scoped to a class. They don’t leak out unless you explicitly make them leak out, and you expose properties/methods to the outside world via some interface. In functional languages, modules perform a similar role - variables/functions are scoped to that module, with something that makes explicit what is exported/exposed to the outside world.

JS used to have neither of these things (it now has modules - note classes in JS are something different, & do not really allow private scoped variables/methods). It does have function scope though, so by using IIFEs, you can make the equivalent of a module which has private variables that do not leak out into the global scope.

// taken from https://addyosmani.com/resources/essentialjsdesignpatterns/book/
var myRevealingModule = (function () {
        var privateVar = "Ben Cherry";
        var publicVar = "Hey there!";
 
        function privateFunction() {
            console.log( "Name:" + privateVar );
        }
 
        function publicSetName( strName ) {
            privateVar = strName;
        }
 
        function publicGetName() {
            privateFunction();
        }
 
        // Reveal public pointers to
        // private functions and properties
 
        return {
            setName: publicSetName,
            greeting: publicVar,
            getName: publicGetName
        };
 
    })();

This pattern allows the syntax of our scripts to be more consistent. It also makes it more clear at the end of the module which of our functions and variables may be accessed publicly which eases readability.

Note that module bundlers often work by basically just compiling JS modules to IIFEs