I have been reading a lot of Javascript lately and I have been noticing that the whole file is wrapped like the following in the .js files to be imported.
(function() { ... code ... })();
What is the reason for doing this rather than a simple set of constructor functions?
An anonymous function is a function that does not have any name associated with it. Normally we use the function keyword before the function name to define a function in JavaScript, however, in anonymous functions in JavaScript, we use only the function keyword without the function name.
In programming languages such as JavaScript, a wrapper is a function that is intended to call one or more other functions, sometimes purely for convenience, and sometimes adapting them to do a slightly different task in the process. For example, SDK Libraries for AWS are examples of wrappers.
An anonymous function is a function that is not stored in a program file, but is associated with a variable whose data type is function_handle . Anonymous functions can accept multiple inputs and return one output. They can contain only a single executable statement.
More generally, the point of using anonymous functions is that they do not require a name, because they are "event handlers" bound to a specific event on a specific object. In this case, the object is the entire Document Object Model, and the anonymous function executes when the entire DOM has loaded.
It's usually to namespace (see later) and control the visibility of member functions and/or variables. Think of it like an object definition. The technical name for it is an Immediately Invoked Function Expression (IIFE). jQuery plugins are usually written like this.
In Javascript, you can nest functions. So, the following is legal:
function outerFunction() { function innerFunction() { // code } }
Now you can call outerFunction()
, but the visiblity of innerFunction()
is limited to the scope of outerFunction()
, meaning it is private to outerFunction()
. It basically follows the same principle as variables in Javascript:
var globalVariable; function someFunction() { var localVariable; }
Correspondingly:
function globalFunction() { var localFunction1 = function() { //I'm anonymous! But localFunction1 is a reference to me! }; function localFunction2() { //I'm named! } }
In the above scenario, you can call globalFunction()
from anywhere, but you cannot call localFunction1
or localFunction2
.
What you're doing when you write (function() { ... })()
, is you're making the code inside the first set of parentheses a function literal (meaning the whole "object" is actually a function). After that, you're self-invoking the function (the final ()
) that you just defined. So the major advantage of this as I mentioned before, is that you can have private methods/functions and properties:
(function() { var private_var; function private_function() { //code } })();
In the first example, you would explicitly invoke globalFunction
by name to run it. That is, you would just do globalFunction()
to run it. But in the above example, you're not just defining a function; you're defining and invoking it in one go. This means that when the your JavaScript file is loaded, it is immediately executed. Of course, you could do:
function globalFunction() { // code } globalFunction();
The behavior would largely be the same except for one significant difference: you avoid polluting the global scope when you use an IIFE (as a consequence it also means that you cannot invoke the function multiple times since it doesn't have a name, but since this function is only meant to be executed once it really isn't an issue).
The neat thing with IIFEs is that you can also define things inside and only expose the parts that you want to the outside world so (an example of namespacing so you can basically create your own library/plugin):
var myPlugin = (function() { var private_var; function private_function() { } return { public_function1: function() { }, public_function2: function() { } } })()
Now you can call myPlugin.public_function1()
, but you cannot access private_function()
! So pretty similar to a class definition. To understand this better, I recommend the following links for some further reading:
EDIT
I forgot to mention. In that final ()
, you can pass anything you want inside. For example, when you create jQuery plugins, you pass in jQuery
or $
like so:
(function(jQ) { ... code ... })(jQuery)
So what you're doing here is defining a function that takes in one parameter (called jQ
, a local variable, and known only to that function). Then you're self-invoking the function and passing in a parameter (also called jQuery
, but this one is from the outside world and a reference to the actual jQuery itself). There is no pressing need to do this, but there are some advantages:
Earlier I described how these functions run automatically at startup, but if they run automatically who is passing in the arguments? This technique assumes that all the parameters you need are already defined as global variables. So if jQuery wasn't already defined as a global variable this example would not work. As you might guess, one things jquery.js does during its initialization is define a 'jQuery' global variable, as well as its more famous '$' global variable, which allows this code to work after jQuery has been included.
In its simplest form, this technique aims to wrap code inside a function scope.
It helps decreases chances of:
It does not detect when the document is ready - it is not some kind of document.onload
nor window.onload
It is commonly known as an Immediately Invoked Function Expression (IIFE)
or Self Executing Anonymous Function
.
var someFunction = function(){ console.log('wagwan!'); }; (function() { /* function scope starts here */ console.log('start of IIFE'); var myNumber = 4; /* number variable declaration */ var myFunction = function(){ /* function variable declaration */ console.log('formidable!'); }; var myObject = { /* object variable declaration */ anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); })(); /* function scope ends */ someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console
In the example above, any variable defined in the function (i.e. declared using var
) will be "private" and accessible within the function scope ONLY (as Vivin Paliath puts it). In other words, these variables are not visible/reachable outside the function. See live demo.
Javascript has function scoping. "Parameters and variables defined in a function are not visible outside of the function, and that a variable defined anywhere within a function is visible everywhere within the function." (from "Javascript: The Good Parts").
In the end, the code posted before could also be done as follows:
var someFunction = function(){ console.log('wagwan!'); }; var myMainFunction = function() { console.log('start of IIFE'); var myNumber = 4; var myFunction = function(){ console.log('formidable!'); }; var myObject = { anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); }; myMainFunction(); // I CALL "myMainFunction" FUNCTION HERE someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console
See live demo.
One day, someone probably thought "there must be a way to avoid naming 'myMainFunction', since all we want is to execute it immediately."
If you go back to the basics, you find out that:
expression
: something evaluating to a value. i.e. 3+11/x
statement
: line(s) of code doing something BUT it does not evaluate to a value. i.e. if(){}
Similarly, function expressions evaluate to a value. And one consequence (I assume?) is that they can be immediately invoked:
var italianSayinSomething = function(){ console.log('mamamia!'); }();
So our more complex example becomes:
var someFunction = function(){ console.log('wagwan!'); }; var myMainFunction = function() { console.log('start of IIFE'); var myNumber = 4; var myFunction = function(){ console.log('formidable!'); }; var myObject = { anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); }(); someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console
See live demo.
The next step is the thought "why have var myMainFunction =
if we don't even use it!?".
The answer is simple: try removing this, such as below:
function(){ console.log('mamamia!'); }();
See live demo.
It won't work because "function declarations are not invokable".
The trick is that by removing var myMainFunction =
we transformed the function expression into a function declaration. See the links in "Resources" for more details on this.
The next question is "why can't I keep it as a function expression with something other than var myMainFunction =
?
The answer is "you can", and there are actually many ways you could do this: adding a +
, a !
, a -
, or maybe wrapping in a pair of parenthesis (as it's now done by convention), and more I believe. As example:
(function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.
or
+function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console
or
-function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console
So once the relevant modification is added to what was once our "Alternative Code", we return to the exact same code as the one used in the "Code Explained" example
var someFunction = function(){ console.log('wagwan!'); }; (function() { console.log('start of IIFE'); var myNumber = 4; var myFunction = function(){ console.log('formidable!'); }; var myObject = { anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; console.log('end of IIFE'); })(); someFunction(); // reachable, hence works: see in the console myFunction(); // unreachable, will throw an error, see in the console myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Read more about Expressions vs Statements
:
One thing one might wonder is "what happens when you do NOT define the variable 'properly' inside the function -- i.e. do a simple assignment instead?"
(function() { var myNumber = 4; /* number variable declaration */ var myFunction = function(){ /* function variable declaration */ console.log('formidable!'); }; var myObject = { /* object variable declaration */ anotherNumber : 1001, anotherFunc : function(){ console.log('formidable!'); } }; myOtherFunction = function(){ /* oops, an assignment instead of a declaration */ console.log('haha. got ya!'); }; })(); myOtherFunction(); // reachable, hence works: see in the console window.myOtherFunction(); // works in the browser, myOtherFunction is then in the global scope myFunction(); // unreachable, will throw an error, see in the console
See live demo.
Basically, if a variable that was not declared in its current scope is assigned a value, then "a look up the scope chain occurs until it finds the variable or hits the global scope (at which point it will create it)".
When in a browser environment (vs a server environment like nodejs) the global scope is defined by the window
object. Hence we can do window.myOtherFunction()
.
My "Good practices" tip on this topic is to always use var
when defining anything: whether it's a number, object or function, & even when in the global scope. This makes the code much simpler.
Note:
block scope
(Update: block scope local variables added in ES6.)function scope
& global scope
(window
scope in a browser environment)Read more about Javascript Scopes
:
Once you get this IIFE
concept, it leads to the module pattern
, which is commonly done by leveraging this IIFE pattern. Have fun :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With