闭包是ES5实现块级私有变量的可靠方式,通过函数作用域隔离变量,外部无法访问或修改;IIFE、工厂函数和Symbol结合闭包可分别实现单例私有、多实例独立及结构化隐蔽。

闭包是 ES5 环境下实现真正块级私有变量的可靠方式——它不依赖语法糖,而是靠函数作用域天然隔离变量,让外部代码既看不到、也改不了内部状态。
用 IIFE 创建独立私有作用域
立即执行函数表达式(IIFE)是最直接的手段:把变量声明在函数体内,只通过返回对象暴露有限接口。
- 变量定义在函数局部作用域中,执行完后不会进入全局,也不会被其他模块干扰
- 返回的对象方法形成闭包,可读写该作用域内的变量,但变量本身不在对象属性链上
- 即使使用 var 声明,只要在 IIFE 内,依然具备“块级”隔离效果
示例:
(function() {
var token = 'a1b2c3';
function auth(user) { return user.key === token; }
window.Api = {
login: function(u) { return auth(u) ? 'ok' : 'denied'; }
};
})();
// token 和 auth 都无法从外部访问
// Api.login({key: 'a1b2c3'}) → 'ok'
// console.log(Api.token) → undefined
工厂函数支持多实例独立状态
若需多个模块实例(如多个计数器),应避免共享闭包,改用每次调用都新建作用域的工厂函数。
- 每次调用工厂函数,都会生成一份全新的私有变量副本
- 各实例间状态完全隔离,互不影响
- 公有方法仍通过闭包绑定各自私有数据,无需 this 或上下文管理
示例:
function createCounter() {
let count = 0;
return {
inc: () => ++count,
val: () => count
};
}
const c1 = createCounter();
const c2 = createCounter();
c1.inc(); c1.inc();
console.log(c1.val(), c2.val()); // 2, 0
结合 Symbol 实现“约定+隔离”双保险
在需要将私有数据挂载到对象上(如兼容某些反射操作)时,可用 Symbol 作键名,配合闭包逻辑增强隐蔽性。
- Symbol 键默认不可枚举,for...in、Object.keys() 都不可见
- 仍需闭包保护读写逻辑,不能仅靠 Symbol —— 它只是防误触,不是防主动探测
- 适合需要部分暴露结构,又希望核心字段不被随意覆盖的场景
示例:
const _data = Symbol('data');
function createUser(name) {
const instance = { name };
instance[_data] = { loginCount: 0 };
return {
login() { instance[_data].loginCount++; },
getCount() { return instance[_data].loginCount; }
};
}
避开常见陷阱
闭包封装看似简单,但几个细节容易导致私有性失效:
- 不要把私有函数直接赋给返回对象,否则外部可直接调用
- 避免用 this 指向动态变化的上下文访问私有变量,优先用词法作用域(箭头函数或闭包内普通函数)
- 命名前缀(如 _secret)纯属提示,不能替代闭包;Object.keys(obj) 看不到闭包变量,但能看到所有 _ 开头属性
- 注意内存:闭包会延长私有变量生命周期,大量长期存活的闭包可能增加内存占用

















