JavaScript 变量提升与作用域面试题总结
1. 变量提升基础题
题目
// 面试题:分析以下代码的执行结果
console.log(a); // ?
console.log(b); // ?
console.log(c); // ?
var a = 1;
let b = 2;
const c = 3;
function test() {
console.log(a); // ?
var a = 4;
console.log(a); // ?
}
test();
答案
console.log(a)→ 输出:undefined(var a被提升为声明,值为undefined)console.log(b)→ 抛出:ReferenceError: Cannot access 'b' before initialization(let b处于暂时性死区)- 由于在第二行抛错,后续的
console.log(c)和test()都不会执行
如果单独看函数:
console.log(a)(函数内第一行)→undefined(函数内var a提升,遮蔽外层的a)console.log(a)(函数内第二行)→4
考点
- 变量提升(Hoisting):
var声明提升到作用域顶部,初始值为undefined - 暂时性死区(TDZ):
let/const存在提升但不可访问,在实际声明前访问会抛ReferenceError - 作用域与遮蔽:函数内
var a遮蔽外层同名变量 - 执行中断:一旦抛出未捕获异常,后续语句不再执行
2. 经典循环闭包题
题目
// 面试题:以下代码输出什么?如何修复?
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i);
}, 100);
}
答案
输出三次 3
原因:var 是函数作用域,三个回调共享同一个 i,循环结束 i 为 3
修复方案
方案1:使用 let(推荐)
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 0, 1, 2
}, 100);
}
方案2:使用闭包(IIFE)
for (var i = 0; i < 3; i++) {
((j) => {
setTimeout(() => {
console.log(j); // 0, 1, 2
}, 100);
})(i);
}
方案3:使用 bind
for (var i = 0; i < 3; i++) {
setTimeout(console.log.bind(null, i), 100); // 0, 1, 2
}
核心原理
var版本:整个循环只有一个i变量,所有回调共享同一引用let版本:每次迭代创建新的块级绑定,每个回调捕获不同的i- 闭包方案:通过参数传递,把当前
i的值作为"快照"传给内层函数
3. var vs let 对比总结
| 特性 | var | let |
|---|---|---|
| 作用域 | 函数作用域(或全局) | 块级作用域 |
| 变量提升 | 提升声明,初始值 undefined | 提升但处于 TDZ,声明前访问报错 |
| 重复声明 | 允许 | 同一作用域内不允许 |
| 全局对象属性 | 会成为全局对象属性 | 不会 |
| for 循环行为 | 所有迭代共享同一变量 | 每次迭代创建新绑定 |
示例对比
// 作用域差异
function test() {
if (true) {
var a = 1;
let b = 2;
}
console.log(a); // 1(var 可访问)
console.log(b); // ReferenceError(let 块外不可访问)
}
// 提升差异
console.log(a); // undefined
console.log(b); // ReferenceError
var a = 1;
let b = 2;
4. 常见陷阱与最佳实践
陷阱1:意外的全局变量
function test() {
a = 1; // 没有声明关键字,成为全局变量!
}
陷阱2:块级作用域误用
if (true) {
var a = 1; // 实际是函数作用域
}
console.log(a); // 1(可能不是预期行为)
最佳实践
- 优先使用
const,需要重新赋值时使用let - 避免使用
var,除非需要兼容老版本浏览器 - 在声明前不要使用变量,避免依赖提升行为
- 使用严格模式(
'use strict')防止意外全局变量
5. 面试要点总结
必须掌握的概念
- 变量提升:提升到当前作用域顶部,不是文件顶部
- 暂时性死区:
let/const的特殊行为 - 作用域链:变量查找规则
- 闭包原理:函数记住其词法作用域
回答技巧
- 先说结果,再解释原因
- 提到关键概念:提升、作用域、TDZ 等
- 给出修复方案,展示解决问题的能力
- 举一反三,说明在实际开发中的应用