立即执行函数(IIFE)
深入理解 JavaScript 的 IIFE
什么是 IIFE
IIFE(立即执行函数表达式),指的是定义后立即执行的函数表达式。它的核心价值在于 创建一个独立的作用域(scope),把临时变量、初始化逻辑、私有状态封装起来,避免污染外部(通常是全局)命名空间。
最常见的形式长这样:
(function () {// 私有作用域const secret = 42;console.log('初始化完成');
})();
或者等价写法:
(function () {// ...
}());
为什么不直接写 function(){ ... }()?因为 function(){} 在开头是 函数声明(function declaration),语法上不能直接被“调用”;把它放在括号里或用其他运算符强制把它当作 表达式(expression),这样就能立即调用。
基本语法变体
常见写法有几类:

要点:
- 把
function放在表达式上下文(例如()或一元运算符)中,保证它是函数表达式而非函数声明。 - 箭头函数也能做 IIFE,但仍需包在
()里或置于能形成表达式的位置。
IIFE 能解决哪些问题?
- 避免全局污染:把临时变量、辅助函数限制在 IIFE 内。
- 模块封装(早期模块化):在 ES Modules 出现前,IIFE 常被用来模拟模块(reveal module pattern)。
- 初始化逻辑:页面或库的启动代码放到 IIFE,保证只运行一次并隐藏内部实现。
- 闭包创建与数据私有化:返回一个对象或函数,外界只能通过暴露的接口访问私有数据。
- 捕获循环变量(旧时用法):在
var循环中用 IIFE 捕获当次循环的变量值(ES6let后常不再需要)。
示例 —
const Counter = (function() {let n = 0; // 私有function inc() { n++; }function get() { return n; }return { inc, get }; // 暴露
}());Counter.inc();
console.log(Counter.get()); // 1
深入:IIFE 与闭包、内存、以及生命周期
- 闭包:IIFE 返回函数或对象并将私有变量保在闭包中。这些私有变量会被 JavaScript 引擎保留,直到没有任何引用存在为止。
- 内存泄露风险:如果 IIFE 创建的闭包持有对大量外部资源(DOM 节点、大数组)的引用且长期存在,会增加内存占用。规范做法:不要在长期存活的闭包中保留不必要的大对象;必要时提供
destroy()方法断开引用。 - 垃圾回收:一旦外部对 IIFE 返回对象的引用被清除,闭包内的私有数据即可被回收。
进阶用法与细节(必读)
1. 命名函数表达式的优势
(function fact(n) {if (n <= 1) return 1;return n * fact(n - 1);
})(5);
这不是函数声明,而是命名函数表达式。fact 名只在函数内部可见,便于递归和调试。
2. 函数声明 vs 函数表达式 —— 常见误区
function foo(){ }(); // SyntaxError —— 这是函数声明,不能直接调用
(function foo(){ }()); // OK
3. 自动分号插入,引起的问题
如果上一行代码没有分号,紧接着写 IIFE 可能被解析到上一行上,导致难以察觉的 bug。常见解决办法:在 IIFE 前加分号(防御性分号):
// 假设上一行没有结束分号
const a = 1
;(function(){ /* safe IIFE */ })()
4. generator IIFE
const g = (function* () {yield 1;yield 2;
})();console.log(g.next());
console.log(g.next());
console.log(g.next().done);

注意:(function*(){})() 返回生成器对象,(生成器对象就是一个迭代器),不是直接迭代结果。
迭代器:
//这是一个简易的迭代器
const iterator = {count: 0,next() {if (this.count < 3) {return { value: this.count++, done: false };}return { value: undefined, done: true };}
};
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
生成器对象
function* fibonacci() {let a = 0, b = 1;while (true) {yield a;// yield b;[a, b] = [b, a + b];}
}
const fib = fibonacci();
for(let i = 0;i < 10;i++){console.log(fib.next().value);
}
每次调用 next() 方法时,生成器会在 yield a 处暂停并产出当前的斐波那契数,然后继续执行循环计算下一个数,从而实现 “按需生成、无限迭代” 的效果

5. 用 IIFE 捕获循环变量
let j = 0;
for (var i = 0; i < 5; i++) {setTimeout(function () {console.log(`第${j+1}次打印`);j++;console.log(i);}, 1000 * i);
}
打印结果:

示例分析:
for (var i = 0; i < 5; i++) {// 赋值 setTimeout(function() { console.log(i) }, 1000 * i)// i 1,2,3,4,5
}
// setTimeout 延迟执行,var i被统一赋值为5
setTimeout(function () {console.log(5);
}, 1000 * 0);
setTimeout(function () {console.log(5);
}, 1000 * 1);
setTimeout(function () {console.log(5);
}, 1000 * 2);
setTimeout(function () {console.log(5);
}, 1000 * 3);
setTimeout(function () {console.log(5);
}, 1000 * 4);
ES5 示例(var):
通过作用域来保护,用块级作用域来保护 i
for (var i = 0; i < 3; i++) {(function(i) {setTimeout(function(){ console.log(i); }, 0);})(i);
}
// 输出 0 1 2
ES6 用 let 更简洁:
for (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 0);
}
测试一下
1.
(function () {if (typeof name === 'undefined') {console.log('Goodbye ' + name);} else {var name = 'Jack';console.log('Hello ' + name);}
})();
解析:var name 被提到函数顶部,且默认为 undefined
2.
var _fn = function () {console.log(1);
};(function () {var _fn = function () {console.log(2);};var fn1 = function () {this._fn.apply(this);};var obj = {_fn: function () {console.log(3);},fn2: fn1.bind({_fn: function () {console.log(4);},}),fn3: fn1,};var fn4 = obj.fn3;var fn5 = obj.fn2;fn1();obj.fn2();obj.fn3();fn4();this.fn5();//在 IIFE(非箭头函数)中,顶层的 this:严格模式下 → undefined,非严格模式下 → 指向全局对象(window)
})();
解析:

题目来源:深入理解JavaScript——立即执行函数(IIFE)
