Functions As Scopes
We’ve seen that we can take any snippet of code and wrap a function around it, and that effectively “hides” any enclosed variable or function declarations from the outside scope inside that function’s inner scope.
For example:
var a = 2;
function foo() { // <-- insert this
var a = 3;
console.log( a ); // 3
} // <-- and this
foo(); // <-- and this
console.log( a ); // 2
While this technique “works”, it is not necessarily very ideal. There are a few problems it introduces. The first is that we have to declare a named-function foo()
, which means that the identifier name foo
itself “pollutes” the enclosing scope (global, in this case). We also have to explicitly call the function by name (foo()
) so that the wrapped code actually executes.
It would be more ideal if the function didn’t need a name (or, rather, the name didn’t pollute the enclosing scope), and if the function could automatically be executed.
Fortunately, JavaScript offers a solution to both problems.
var a = 2;
(function foo(){ // <-- insert this
var a = 3;
console.log( a ); // 3
})(); // <-- and this
console.log( a ); // 2
Let’s break down what’s happening here.
First, notice that the wrapping function statement starts with (function...
as opposed to just function...
. While this may seem like a minor detail, it’s actually a major change. Instead of treating the function as a standard declaration, the function is treated as a function-expression.
Note: The easiest way to distinguish declaration vs. expression is the position of the word “function” in the statement (not just a line, but a distinct statement). If “function” is the very first thing in the statement, then it’s a function declaration. Otherwise, it’s a function expression.
The key difference we can observe here between a function declaration and a function expression relates to where its name is bound as an identifier.
Compare the previous two snippets. In the first snippet, the name foo
is bound in the enclosing scope, and we call it directly with foo()
. In the second snippet, the name foo
is not bound in the enclosing scope, but instead is bound only inside of its own function.
In other words, (function foo(){ .. })
as an expression means the identifier foo
is found only in the scope where the ..
indicates, not in the outer scope. Hiding the name foo
inside itself means it does not pollute the enclosing scope unnecessarily.
Anonymous vs. Named
You are probably most familiar with function expressions as callback parameters, such as:
setTimeout( function(){
console.log("I waited 1 second!");
}, 1000 );
This is called an “anonymous function expression”, because function()...
has no name identifier on it. Function expressions can be anonymous, but function declarations cannot omit the name — that would be illegal JS grammar.
Anonymous function expressions are quick and easy to type, and many libraries and tools tend to encourage this idiomatic style of code. However, they have several draw-backs to consider:
Anonymous functions have no useful name to display in stack traces, which can make debugging more difficult.
Without a name, if the function needs to refer to itself, for recursion, etc., the deprecated
arguments.callee
reference is unfortunately required. Another example of needing to self-reference is when an event handler function wants to unbind itself after it fires.Anonymous functions omit a name that is often helpful in providing more readable/understandable code. A descriptive name helps self-document the code in question.
Inline function expressions are powerful and useful — the question of anonymous vs. named doesn’t detract from that. Providing a name for your function expression quite effectively addresses all these draw-backs, but has no tangible downsides. The best practice is to always name your function expressions:
setTimeout( function timeoutHandler(){ // <-- Look, I have a name!
console.log( "I waited 1 second!" );
}, 1000 );
Invoking Function Expressions Immediately
var a = 2;
(function foo(){
var a = 3;
console.log( a ); // 3
})();
console.log( a ); // 2
Now that we have a function as an expression by virtue of wrapping it in a ( )
pair, we can execute that function by adding another ()
on the end, like (function foo(){ .. })()
. The first enclosing ( )
pair makes the function an expression, and the second ()
executes the function.
This pattern is so common, a few years ago the community agreed on a term for it: IIFE, which stands for Immediately Invoked Function Expression.
Of course, IIFE’s don’t need names, necessarily — the most common form of IIFE is to use an anonymous function expression. While certainly less common, naming an IIFE has all the aforementioned benefits over anonymous function expressions, so it’s a good practice to adopt.
var a = 2;
(function IIFE(){
var a = 3;
console.log( a ); // 3
})();
console.log( a ); // 2
There’s a slight variation on the traditional IIFE form, which some prefer: (function(){ .. }())
. Look closely to see the difference. In the first form, the function expression is wrapped in ( )
, and then the invoking ()
pair is on the outside right after it. In the second form, the invoking ()
pair is moved to the inside of the outer ( )
wrapping pair.
These two forms are identical in functionality. It’s purely a stylistic choice which you prefer.
Another variation on IIFE’s which is quite common is to use the fact that they are, in fact, just function calls, and pass in argument(s).
For instance:
var a = 2;
(function IIFE( global ){
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
})( window );
console.log( a ); // 2
We pass in the window
object reference, but we name the parameter global
, so that we have a clear stylistic delineation for global vs. non-global references. Of course, you can pass in anything from an enclosing scope you want, and you can name the parameter(s) anything that suits you. This is mostly just stylistic choice.
Another application of this pattern addresses the (minor niche) concern that the default undefined
identifier might have its value incorrectly overwritten, causing unexpected results. By naming a parameter undefined
, but not passing any value for that argument, we can guarantee that the undefined
identifier is in fact the undefined value in a block of code:
undefined = true; // setting a land-mine for other code! avoid!
(function IIFE( undefined ){
var a;
if (a === undefined) {
console.log( "Undefined is safe here!" );
}
})();
Still another variation of the IIFE inverts the order of things, where the function to execute is given second, after the invocation and parameters to pass to it. This pattern is used in the UMD (Universal Module Definition) project. Some people find it a little cleaner to understand, though it is slightly more verbose.
var a = 2;
(function IIFE( def ){
def( window );
})(function def( global ){
var a = 3;
console.log( a ); // 3
console.log( global.a ); // 2
});
The def
function expression is defined in the second-half of the snippet, and then passed as a parameter (also called def
) to the IIFE
function defined in the first half of the snippet. Finally, the parameter def
(the function) is invoked, passing window
in as the global
parameter.