Suggest an editImprove this articleRefine the answer for “What is IIFE (immediately invoked Function expression) in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**IIFE** is a function that runs immediately when the parser reaches it, creating a private scope that disappears after execution. ```javascript (function() { var secret = "hidden"; console.log(secret); // "hidden" })(); console.log(typeof secret); // "undefined" - not in global scope ``` **Key point:** outer parens make it an expression; trailing `()` invoke it. Variables inside don't reach global scope.Shown above the full answer for quick recall.Answer (EN)Image**IIFE (Immediately Invoked Function Expression)** is a function that defines and runs itself in one shot, creating a private scope that disappears after execution. ## Theory ### TL;DR - IIFE is like a soundproof booth: code runs inside, variables stay inside, nothing leaks to global scope - Main difference from regular functions: runs once automatically when the parser hits it, not when you call it - Use it for one-time init code, private variables, or passing globals safely into a contained scope - Post-ES6: `import/export` and `let` in blocks cover most cases, but IIFE still appears in legacy code and Babel output ### Quick example ```javascript // Without IIFE - var leaks to global scope var count = 0; console.log(count); // 0 - visible everywhere // With IIFE - scope stays contained (function() { var count = 0; count++; console.log(count); // 1 })(); console.log(typeof count); // "undefined" - didn't leak ``` The outer parens turn a function declaration into an expression. The trailing `()` invoke it immediately. That's the whole syntax. ### How scope isolation works When the JavaScript engine parses an IIFE, it creates a new execution context with its own variable environment. The function runs, variables live inside that context, and when it finishes the context is discarded. Variables die with it. Nothing reaches the outer scope unless you explicitly return or assign something outward. This is also why `var` inside an IIFE doesn't pollute `window`: the variable exists in the function's scope, not the global one. The same mechanism that powers [closures](/questions/what-is-closure-in-javascript) is at work here. ### When to use - One-time initialization (config setup, polyfill loading) - run once, no lingering variables - Passing globals safely: `(function($) { ... })(jQuery)` pins the `$` alias regardless of external conflicts - Legacy module patterns like AMD/UMD wrappers, before ES6 modules were available - Wrapping third-party scripts where you can't control the global namespace Skip it when you have ES6 modules (`import/export`), or when you just need block scope (`{ let x = 1; }`). ### How the engine handles it The outer parens tell the parser: this is an expression, not a declaration. Without them, `function() {}()` throws a SyntaxError because the parser expects a name after `function`. V8 then adds the IIFE to the call stack, runs it in a fresh execution context, and pops it off. The scope chain it created is gone. Any [closures](/questions/what-is-closure-in-javascript) inside (like `setTimeout` callbacks) keep a reference to that scope alive, but no outside code can reach it directly. ### Common mistakes **Forgetting the outer parens:** ```javascript function() { console.log("hi"); }(); // SyntaxError ``` The parser sees a function declaration without a name. Fix: wrap in parens `(function() { ... })();` **Assuming IIFE prevents all leaks:** ```javascript (function() { notDefined = 1; // No var/let/const! })(); console.log(notDefined); // 1 - leaked to global ``` Without `var`, `let`, or `const`, the assignment creates a global. Add `"use strict"` as the first line inside the IIFE to catch this. **Recursive IIFE using arguments.callee:** ```javascript // Broken in strict mode var fib = (function(n) { if (n <= 1) return n; return arguments.callee(n-1) + arguments.callee(n-2); // Deprecated })(10); // Fix: use a named function expression var fib = (function f(n) { return n <= 1 ? n : f(n-1) + f(n-2); })(10); ``` `arguments.callee` is blocked in strict mode. The named function expression `f` refers to itself without that problem. **Using IIFE for loop closures when `let` exists:** ```javascript // Old way - IIFE to capture loop variable for (var i = 0; i < 3; i++) { (function(j) { setTimeout(() => console.log(j), 0); })(i); } // Modern way - just use let for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } ``` ### Real-world usage - jQuery plugins: `(function($) { $.fn.myWidget = function() { ... }; })(jQuery)` was the standard pattern for years - Lodash/Underscore source: IIFE passes `root` and `freeGlobal` for AMD/CommonJS detection - Babel output: still wraps transpiled modules in IIFE for browser scope isolation - Polyfill scripts: `(function() { if (!window.Promise) { /* load polyfill */ } })();` I saw this pattern constantly in pre-ES6 codebases. Once ES6 modules landed in browsers, new projects stopped writing IIFEs, but maintenance work still means reading them. ### Follow-up questions **Q:** Write an IIFE that returns a counter function. **A:** `var counter = (function() { var count = 0; return function() { return ++count; }; })();` The IIFE runs once and returns the inner function. `count` stays alive because the returned function closes over it: `counter()` returns 1, then 2, and so on. **Q:** Why did IIFEs matter before ES6? **A:** `var` has function scope, not block scope. In large apps with many script files, every top-level `var` became a property on `window`. IIFE was the only reliable way to create a private scope without a module system. **Q:** What's the difference between arrow IIFE and classic IIFE? **A:** `(() => { ... })()` works for most cases. Arrow functions don't bind their own `this` or `arguments`, so the classic function form is needed when the code inside relies on those. **Q:** A `setTimeout` callback inside an IIFE runs after the IIFE finishes. Does it keep the scope alive? **A:** Yes. The callback closes over the IIFE's scope. The execution context is gone, but the closure keeps the variable bindings alive until the callback runs and gets garbage collected. **Q:** (Senior) How would you write a UMD wrapper using the IIFE pattern for a no-bundler environment? **A:** `(function(root, factory) { if (typeof define === 'function') define(factory); else root.MyLib = factory(); })(this, function() { return { /* public API */ }; });` It detects AMD at runtime and falls back to attaching the library to global scope directly. ## Examples ### Basic: scope isolation ```javascript (function() { var privateVar = "I exist only here"; console.log(privateVar); // "I exist only here" })(); console.log(typeof privateVar); // "undefined" - gone after IIFE ran ``` The variable is created, used, and discarded when the IIFE's execution context is destroyed. No trace in global scope. ### Real-world: jQuery plugin with safe $ alias ```javascript // Passing jQuery as an argument fixes the $ alias // even if another library also claims $ (function($) { $.fn.tooltip = function(options) { return this.each(function() { var el = $(this); el.attr("title", options.text || "default"); }); }; })(jQuery); // Usage $(".btn").tooltip({ text: "Click me" }); ``` This is the pattern DataTables, Select2, and most jQuery plugins used. The `$` inside the IIFE always refers to jQuery, regardless of what `$` means in the outer scope. The argument injection pattern is what made this safe across mixed codebases.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.