IIFE, Module Pattern & Pre-ES6 Privacy
Function scope hi privacy tha — IIFE se andar band karo, bahar dikhne mat do.
An IIFE (Immediately Invoked Function Expression) is a function that is defined and called in the same expression. It creates a new scope immediately — variables inside are private to that scope.
// Basic IIFE — two valid syntaxes
(function() {
var private = 'only here';
console.log('IIFE running!');
console.log(private);
})();
// console.log(private); // ReferenceError — scope gone!
// Arrow IIFE
(() => {
const secret = 42;
console.log('Arrow IIFE:', secret);
})();
// IIFE with return value
const result = (function() {
const x = 10;
const y = 20;
return x + y;
})();
console.log(result); // 30 — IIFE is done, but value escaped
// IIFE with parameters
(function(name, greeting) {
console.log(greeting + ', ' + name + '!');
})('Sai', 'Namaste');
// Why outer parens are needed:
// function() { }(); // SyntaxError — declaration cannot be invoked
// (function() { })(); // OK — expression, not declaration
// !function() { }(); // also OK — unary operator forces expression context
// +function() { }(); // also OK — unary + forces expression
Before ES6 modules: every <script> tag shared the same global scope. Multiple library scripts — all variables on window — name collisions!
Classic collision: jQuery used $, Prototype.js also used $ — war!
jQuery's fix: wrapped the entire library in an IIFE, exposing only ONE name to global. Everything else was private inside the closure.
// The global collision problem (pre-ES6)
// Script 1 loads:
var utils = 'script1 utils'; // on window!
// Script 2 loads:
var utils = 'script2 utils'; // OVERWRITES script1!
// Script 1's utils is gone — silent bug
// IIFE solution: only expose what you need
var MyLib = (function() {
var version = '1.0.0';
var privateCache = {};
function privateHelper(key) {
return privateCache[key] || null;
}
return {
get: function(key) { return privateHelper(key); },
set: function(key, val) { privateCache[key] = val; },
getVersion: function() { return version; }
};
})();
MyLib.set('user', { name: 'Sai' });
console.log(MyLib.get('user')); // { name: 'Sai' }
console.log(MyLib.getVersion()); // '1.0.0'
// MyLib.privateCache // undefined — private!
// MyLib.privateHelper // undefined — private!
// jQuery used this EXACT pattern:
// var jQuery = (function(window, undefined) {
// var jQuery = function(selector) { ... };
// return jQuery;
// })(window);
It made window a local variable (faster lookup in scope chain) and guaranteed that 'undefined' was actually undefined — in old browsers, undefined could be reassigned! This is the level of paranoia required in pre-strict-mode JS.
The Module Pattern is an IIFE that returns an object — the public interface. Private variables and functions live inside the IIFE closure. Public methods are properties of the returned object.
// Full module pattern — counter with private state
var Counter = (function() {
// Private state
var count = 0;
var step = 1;
var history = [];
// Private function
function logHistory(action) {
history.push({ action: action, value: count, time: Date.now() });
}
// Public API
return {
increment: function() {
count += step;
logHistory('increment');
return count;
},
decrement: function() {
count -= step;
logHistory('decrement');
return count;
},
setStep: function(n) { step = n; },
reset: function() {
count = 0;
logHistory('reset');
return count;
},
getHistory: function() { return history.slice(); },
value: function() { return count; }
};
})();
Counter.increment(); // 1
Counter.increment(); // 2
Counter.setStep(5);
Counter.increment(); // 7
console.log(Counter.value()); // 7
console.log(Counter.getHistory()); // [{...}, ...]
// Counter.count — undefined (private)
// Counter.history — undefined (private)
// Module augmentation — add features from another file
var Counter = (function(module) {
module.double = function() {
return module.increment() + module.increment();
};
return module;
})(Counter || {});
ES6 modules made IIFEs largely unnecessary for scope isolation. Each file has its own scope automatically. Block scope with let/const replaces simple IIFEs. Class private fields (#) replace IIFE for per-instance private data.
// Before ES6 — IIFE for privacy:
var module = (function() {
var private = 'secret';
return { get: function() { return private; } };
})();
// ES6 module (file has its own scope automatically):
// module.js — no IIFE needed!
const private = 'secret'; // not on window
export const get = () => private;
// Block scope replaces simple IIFE for one-off isolation:
// Old way:
(function() {
var temp = expensiveComputation();
doSomethingWith(temp);
})();
// New way — block scope:
{
const temp = expensiveComputation();
doSomethingWith(temp);
}
// Async IIFE — still very useful in 2024:
(async () => {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (err) {
console.error('Failed:', err);
}
})();
// Class private fields (ES2022):
class BankAccount {
#balance = 0; // truly private
deposit(n) { this.#balance += n; }
get balance() { return this.#balance; }
}
Legacy codebases are full of IIFE patterns. Here are the ones you'll encounter most often:
// 1. jQuery plugin — safe $ alias
(function($) {
$.fn.highlight = function(color) {
return this.css('background-color', color || 'yellow');
};
})(jQuery);
// 2. UMD pattern — works everywhere
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['exports'], factory); // AMD (RequireJS)
} else if (typeof module !== 'undefined' && module.exports) {
factory(module.exports); // CommonJS (Node.js)
} else {
factory(root.MyLib = {}); // Global browser
}
})(this, function(exports) {
exports.version = '1.0.0';
exports.greet = function(name) { return 'Hello, ' + name; };
});
// 3. Init-time branching — decide ONCE, not every call
var addEvent = (function() {
if (window.addEventListener) {
return function(el, type, fn) {
el.addEventListener(type, fn, false);
};
} else {
return function(el, type, fn) {
el.attachEvent('on' + type, fn); // IE8 fallback
};
}
})();
// 4. Namespace pattern with IIFE
var App = App || {};
App.utils = (function() {
var version = '2.0';
return {
getVersion: function() { return version; },
formatDate: function(d) { return d.toISOString().slice(0, 10); }
};
})();
Lo kar liya — Key Points:
- ✅ IIFE = function defined and immediately called — creates a private scope that dies instantly but can return values
- ✅ The outer () change the function from a declaration to an expression — required for immediate invocation
- ✅ Module pattern = IIFE returning an object — private state inside closure, public API on the returned object
- ✅ Before ES6, IIFE was the ONLY way to create private scope — no block scope, no modules, no class private fields
- ✅ ES6 module files have their own scope automatically — no IIFE needed for isolation in modern code
- ✅ Async IIFE
(async () => await ... )()is still useful in 2024 when top-level await is unavailable
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login