模块化雏形:IIFE(立即执行函数表达式)
问题背景:为什么需要 IIFE?
在 2000s 的“动态交互时代”,随着前端业务逻辑逐渐复杂,JavaScript 代码也越来越长。
但是 JS 当时有个很大的问题:
- 没有模块化机制:所有 JS 文件都被加载到全局作用域里。
- 变量名冲突:不同文件里的变量、函数很容易相互覆盖。
- 污染全局命名空间:引入多个库(比如 jQuery + Prototype + 自己的脚本)时,常常发生冲突。
👉 于是,工程师们想:能不能人为制造一个“私有作用域”,把变量包起来?
IIFE 是什么?
IIFE 是 一种函数写法,它的核心思想是:
声明一个函数,并立即执行它,用这个函数的作用域来隔离变量。
语法:
(function () {
// 这里的变量不会泄漏到全局作用域
var msg = "hello IIFE";
console.log(msg);
})();
执行效果:
- 函数被立刻调用
- 函数内部形成 独立作用域
- 外部访问不到内部变量
IIFE 的原理
IIFE 的原理依赖于 JS 的两个机制:
- 函数作用域(Function Scope)
- 在函数内部声明的变量,只能在函数里访问。
- 执行完函数后,变量会被回收(除非闭包引用)。
- 表达式立即执行
- JavaScript 允许你在函数定义外面套括号,使它变成“函数表达式”。
- 这个表达式写完就可以立刻执行。
// 普通函数声明
function test() {
console.log("普通函数");
}
// IIFE:变成函数表达式并立即执行
(function() {
console.log("IIFE 立即执行函数");
})();
IIFE 的典型用法
(1) 防止变量冲突
// 文件 A
(function () {
var version = "1.0";
console.log("A 库", version);
})();
// 文件 B
(function () {
var version = "2.0";
console.log("B 库", version);
})();
即使都用了 version 变量,互不干扰。
(2) 封装库
早期很多库(如 jQuery)都是 IIFE 写法:
var $ = (function () {
function $(selector) {
return document.querySelector(selector);
}
return $;
})();
外部只暴露 $,内部细节都隐藏。
(3) 创建模块
IIFE 就像“最原始的模块系统”:
var Counter = (function () {
var count = 0;
return {
add: function () { count++; return count; },
reset: function () { count = 0; }
};
})();
console.log(Counter.add()); // 1
console.log(Counter.add()); // 2
Counter.reset();
👉 内部变量 count 是私有的,外部无法直接访问,达到了 封装 的目的。
IIFE 的局限性
虽然 IIFE 解决了早期 命名冲突 和 作用域隔离 的问题,但它仍有缺陷:
- 无法在多个文件中 优雅地依赖其他模块(比如 A 模块想用 B 模块,只能靠全局变量传递)。
- 无法 按需加载,所有代码要么一次性全引入,要么手动管理
<script>顺序。 - 难以维护,项目一大,IIFE 嵌套、全局变量依赖会变得非常混乱。
👉 这些缺陷推动了 CommonJS、AMD、ES Module 等真正的模块化标准的出现。
总结一下:
IIFE 是前端模块化的雏形,它通过函数作用域来隔离变量,避免全局污染,为后来的模块化标准打下了实践基础。