iife的核心价值是隔离变量声明,防止意外泄漏到全局作用域;正确写法为(function(){})()或(() => {})(),传参可优化压缩与性能,但不解决异步闭包陷阱或全局副作用。

为什么变量会意外泄漏到全局作用域
在非严格模式下,直接写 varName = 'hello' 而不加 var/let/const,这个变量就挂到了 window(浏览器)或 global(Node.js)上。多人协作时,一个模块里漏写声明符,可能悄悄覆盖另一个模块的同名变量——你改完自己逻辑,别人页面突然报错 TypeError: someFunction is not a function,查半天发现是被你定义的同名 someFunction 覆盖了。
IIFE 的核心价值不是“炫技”,是给一段逻辑划出明确边界:里面声明的 var、let、const 全部关在里面,外面看不见,也碰不到。
怎么写一个安全、可读、兼容性好的 IIFE
最稳妥的写法是括号包裹函数表达式,再立即调用。别用 function foo() {}() 这种——语法错误,JS 引擎会把它当函数声明,而函数声明不能紧跟 ()。
- ✅ 正确:
(function () { /* code */ }());或(function () { /* code */ })(); - ✅ 现代写法(推荐):
(( ) => { /* code */ })();—— 箭头函数更短,但注意它没有自己的this和arguments - ❌ 错误:
function () { }();(缺少外层括号,解析为函数声明) - ⚠️ 注意:
!function () { }();可行,但!会把返回值转成布尔,如果内部 return 了真值,外部拿到的是true,容易埋坑
IIFE 传参和避免闭包陷阱
经常需要把 window、document 或 jQuery 实例传进去,既压缩友好(UglifyJS 能把形参名压缩),又避免反复查作用域链。
常见写法:(function (global, doc, $) { /* 使用 global/document/$ */ })(window, document, jQuery);
- 传入
window后,在函数内用global访问,压缩后变成单字母,体积小 - 如果内部用了
setTimeout或事件回调,记得:IIFE 本身不解决异步中的this或变量捕获问题;for (var i = 0; i console.log(i), 0); }仍会输出三个3——这不是 IIFE 的责任,得用let或闭包手动绑定 - 不要在 IIFE 里 return 大对象并赋值给全局变量,比如
var utils = (function () { return {foo(){}} })();——这等于又把东西放回全局了,隔离白做了
现代项目里还要手写 IIFE 吗
基本不用了。ES Module(import/export)天然提供作用域隔离,Webpack/Vite 打包时每个模块自动被包裹进类似 IIFE 的函数中。但以下场景仍可能遇到或需要手写:
- 维护老项目(IE11 兼容需求,没模块系统)
- 写一个独立的、要塞进
<script></script>标签的轻量工具(比如统计脚本、A/B 测试 snippet) - 在 Node.js 的 CommonJS 模块里临时隔离一段逻辑(虽然
require本身已隔离,但有时需要更细粒度控制) - 调试时快速建沙盒:
(function () { debugger; /* 临时跑几行代码 */ })();,比开控制台还干净
真正容易被忽略的是:IIFE 只隔离「声明」,不隔离「副作用」。比如你在里面调了 document.write()、绑了全局事件、修改了 Array.prototype——这些动作照样影响整个页面。作用域关住了变量,关不住行为。










