Immediately invoked function expression
Wow, that's a mouthful. According to wikpedia, an IFFE is ยซa JavaScript programming language idiom which produces a lexical scope using JavaScript's function scoping.ยป
But let's assume lexical scope, variable hoisting and function scoping are not terms you are confortable with yet, don't worry, let's see what an IFFE actually does, with examples, one bit at a time.
Basically and IIFE is:
- a function
- evaluated immediately
- returning an object
- with public attributes (methods and values)
- that can refer to non public ones
- and not exposing the private ones
So the simplest form is this:
f1 = function(){
let secret_a = 1;
function secret_b(){
return 2;
}
return { public_a: secret_a, public_b: secret_b };
}
let obj1 = f1()
console.log('obj1.public_a: ' + obj1.public_a); // obj1.public_a: 1
console.log('obj1.public_b: ' + obj1.public_b()); // obj1.public_b() 2
console.log('obj1.secret_a: ' + typeof(obj1.secret_a)); // obj1.secret_a: undefined
console.log('obj1.secret_b: ' + typeof(obj1.secret_b)); // obj1.secret_b: undefined
Now imagine we replace f1
with everything that was on the right of f1 =
let obj2 = (
// here starts f1
function(){
let secret_a = 1;
function secret_b(){
return 2;
}
return { public_a: secret_a, public_b: secret_b };
}
// here ends f1
)()
console.log('obj2.public_a: ' + obj2.public_a); // obj2.public_a: 1
console.log('obj2.public_b: ' + obj2.public_b()); // obj2.public_b() 2
console.log('obj2.secret_a: ' + typeof(obj2.secret_a)); // obj2.secret_a: undefined
console.log('obj2.secret_b: ' + typeof(obj2.secret_b)); // obj2.secret_b: undefined
This has the same effect, and we already recognise the IIFE famous form.
(function(){ ... })()
But an IIFE can not only return a new object, it can add stuff to one:
let obj3 = { prop1: 3 };
let obj4 = (function(expose){ // we call expose what comes from outside
function secret_b(){
return 2;
}
expose.public_b = function(){ // we add properties to expose
return secret_b() + expose.prop1; // and read from it
}
return expose; // we return the received object with extra stuff
})(obj3); // we call the IIFE with some object
console.log('obj4.prop1: ' + obj4.prop1); // obj4.prop1: 3
console.log('obj4.public_b: ' + obj4.public_b()); // obj4.public_b() 5
Observe there were four changes here:
-
let obj4 = (function(expose){
we name the argument we expect -
expose.public_b = function(){
we add stuff to the received object -
return expose;
we return the anriched object -
})(obj3);
we call the IIFE with an argument from outside
But nowadays, everyone is loading multiple files having complex pipelines, and here IIFES can help you by being able to enrich them selves:
// file 1
MyObj = (function(expose){
let secret_b = 4;
expose.public_b = function(){
return secret_b;
}
return expose;
})(window.MyObj || {});
// file 2
MyObj = (function(expose){
expose.public_c = function(){
return expose.public_b() + 5;
}
return expose;
})(window.MyObj || {});
console.log('myObj.secret_b: ' + typeof(MyObj.secret_b)); // myObj.secret_b(): undefined
console.log('myObj.public_b: ' + MyObj.public_c()); // myObj.public_b() 9
This works for any order file 1
and file 2
get loaded, so you can have some basic/shared stuff in your object and augment it as needed on certain pages but only if an when the user loads them.
This can get pretty crazy soon, so it's better to impose some conventions on it, so you know what to expect and where to put things:
// Use you company or app name here to keep or your stuff namespaced
// separatedly from 3rd party libraries
window.Namespace = window.Namespace || {};
// Componen documentation
// What it does: functional description
// Example usage:
// Namespace.Component.init({ selector: '#some_id', some_param: 30 });
window.Namespace.Component = (function(expose){
let private_some;
function private_helper(){
// stuff
}
expose.public_method = function(){
// stuff
}
expose.init = function(options){
private_some = option.some;
}
return expose;
})(window.Namespace.Component || {});
Then you can use:
Namespace.Component.init({
selector: '#some_id',
some_param: 30
});
On your html files so you have the selector definition and reference on the same place, easy to modify if the html needs to change.
I would additionally recommend to always use 'js-xxxx'
-style classes and ids (or data-attributes) so that they do not interfere with design/layout tasks.
So, what do you think? do you use a similar pattern already? did this help you wrap your head around IIFE's. Is there anything here which purpose is still not clear to you.
Top comments (0)