作用域与闭包
作用域是变量的可见范围。JS 历经三个阶段:全局作用域 / 函数作用域(var 时代)→ 块级作用域(let / const 时代)。var 声明会提升到作用域顶部且值为 undefined、可重复声明;let / const 也有提升但处于“暂时性死区(TDZ)”,声明前访问直接抛 ReferenceError。
闭包是“函数 + 它创建时所在的词法作用域”的组合:内层函数引用了外层变量,外层函数 return 后这些变量依然存活。用途:模块化(IIFE 封装私有变量)、函数柯里化、缓存。经典陷阱:for + var + setTimeout 打印全是终值,因为循环里所有回调共享同一个 i;改 let 或 IIFE 传参即可。
- var 提升可重复声明,let / const 有暂时性死区、块级作用域
- 闭包 = 函数 + 创建时的词法作用域;外层返回后内部变量仍存活
- for + setTimeout 经典题:var 全打印终值,改 let 或用 IIFE 传参解决
JS
// 1. 变量提升与暂时性死区 console.log(a); // undefined(var 提升) var a = 1; try { console.log(b); } catch (e) { console.log('TDZ:', e.message); } // ReferenceError let b = 2; // 2. 闭包:外层返回后 count 依然存活(模块模式) function counter() { let count = 0; // 私有变量:外部拿不到,只能靠闭包操作 return { inc: () => ++count, get: () => count, }; } const c = counter(); c.inc(); c.inc(); console.log('count =', c.get()); // 2 // 3. 经典面试题:var 打印 3 个 3,let 打印 0 1 2 for (var i = 0; i < 3; i++) { setTimeout(() => console.log('var:', i), 0); // 3 3 3(共享同一个 i) } for (let j = 0; j < 3; j++) { setTimeout(() => console.log('let:', j), 0); // 0 1 2(每轮独立作用域) } // 4. IIFE 立即执行函数:最古老的模块化与闭包应用 const secret = (() => { const key = 'hidden'; return { getKey: () => key }; })(); console.log(secret.getKey()); // hidden
本节要点闭包的本质是词法作用域的延续:函数带走它出生时的变量;var / let 的区别就在提升与块级作用域。




