Reputation: 15372
I was reading this tutualial on javascriptissexy.com. I followed up until the last example.
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function (j) { // the j parametric variable is the i passed in on invocation of this IIFE
return function () {
return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
} (); // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.
} (i); // immediately invoke the function passing the i variable as a parameter
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id); // 100
var cruiseID = createIdForActionCelebs [1];
console.log(cruiseID.id); // 101
First, I didn't see an IIFE, or at least in the syntax I am familiar with in which there would be a (function()...
with the leading left-side parenthesis. Also,
return function () {
return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
} ();
Why these two returns
? And if this is meant to be an IIFE doesn't need the surrounding parenthesis? Also, how does not j
immediately increment to the length of the array as i
did in the previous example? Concerning the line theCelebrities[i]["id"] = function (j) {
which accesses the element id
of the actionCelebs
parameter. Why does that property need initially to be set to 0
for each element.
Previous example
// This example is explained in detail below (just after this code box).
function celebrityIDCreator (theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function () {
return uniqueID + i;
}
}
return theCelebrities;
}
var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
var stalloneID = createIdForActionCelebs [0];
console.log(stalloneID.id()); // 103
Here all of the values are set to 103
Upvotes: 0
Views: 189
Reputation: 35314
Answers to your questions:
IIFE stands for Immediately Invoked Function Expression. In JavaScript, function literals occur in one of two forms: (1) full function definition statements, or (2) expressions. A full function definition statement has function
as the leading token of the statement, which means that the statement is not an expression, but just a function definition. It effectively causes a local (or global if in global scope) variable to be created in the current scope with a name equal to the function name as given in the function definition statement, and a value equal to a reference to the function:
function myFunction() {}
myFunction();
When function
occurs anywhere (not as the first token) inside an expression, the function definition is said to be "expressionized" and does not automatically create any local variable, but can be stored in a variable by assigning it, or called immediately by following it with a pair of parentheses:
var func = function() { return 7; };
alert(func()); // 7
var funcRes = function() { return 3; }();
alert(funcRes); // 3
Strictly speaking, the term IIFE can refer to any context where a function definition is expressionized and then immediately invoked by following it with a pair of parentheses. You can see in your code example that they do this with the function returning uniqueID + j
, and they also do it with the function that encloses said function and simply relays its return value to its parent scope, so both qualify as an IIFE.
In my experience, the term IIFE is used most often to refer to an IIFE that comprises an entire statement, and thus the leading left parenthesis is necessary to exressionize the function definition, for example:
(function() {
var localVar = 3;
alert(localVar);
})();
I think that's why you found the code example slightly unexpected. But both of those 2 function definitions in your code example certainly qualify as IIFEs according to the strict definition of what an IIFE is; as long as the function
keyword does not occur as the first token in the statement, then it is expressionized, and can be invoked immediately to form an IIFE.
The two return
s are simply necessary as part of the design of the code. Admittedly, the whole thing is quite contrived, and there is a much easier way to accomplish the task, namely, a simple loop that loops through the array and assigns the value of the id
hash key to a number incrementing from uniqueID
, but they're trying to demonstrate IIFEs, I suppose. The inner return expression computes the next id value from uniqueID
and j
, which are both closured by the inner function, and returns it from the inner IIFE, and then the outer return relays that return value from the outer IIFE back to the celebrityIDCreator
function scope, where it can be assigned.
No, IIFEs do not require surrounding parentheses. To expressionize a function, all that is required is that the function
keyword not occur as the first token of the statement.
The full process of closuring is as follows: When a function is defined and contains a variable whose identifier does not bind to any local variable (variable declared with var
or function parameter), then it closures around the closest ancestral (thinking of the parse tree) local with that same identifier, or the global if it cannot be bound to any ancestral local. The important point is this: It's a variable reference that is captured by the closure, not a variable value.
In the "Previous example" i
closures around the local i
that lives in the celebrityIDCreator
function scope. Importantly, the function is not immediately invoked, but a reference to the function is stored as the actual value of the id
hash key. It is invoked later, during printing. That means that when the function is finally invoked, the variable i
has already finished looping through the array, and is now retaining its final value, 103. If the function had been invoked immediately, then i
would actually be evaluated during the loop and it would resolve to its current value, which would be the correct incrementing value.
In your main code example, j
in the inner IIFE is closuring around the function parameter of the outer IIFE, which is temporary and will never be incremented; that parameter j
is a distinct variable from the i
in celebrityIDCreator
function scope, which is never closured. Also, the inner IIFE is being immediately invoked, so it resolves to the variable's current value anyway. So they've actually overdone it here; there are two reasons why the final assigned value is the proper incrementing value and not the max value (103).
Main point: Closures closure around variables, not values.
id
property did not need to be set to 0 initially; that appears to be just a placeholder. It is overwritten (in both your main example and in the "Previous example") when it is assigned in celebrityIDCreator
. And since in the "Previous example" it is overwritten not by another number but by a function, it is a little weird. Again, the whole thing is a little contrived (no offense to the authors...), but all the concepts are there.Upvotes: 2