你不知道的 JavaScript(上卷) 作用域与提升机制

58 阅读2分钟

《你不知道的 JavaScript(上卷)》第 3-4 章核心笔记:作用域与提升机制

作用域不是“变量在哪里声明”,而是“变量在哪里可被访问”。理解函数作用域、块作用域与变量提升,是掌握 JS 执行机制的关键。


一、函数作用域 vs 块作用域(第 3 章)

1. 函数作用域是传统主力

  • var 声明的变量属于所在函数的作用域
  • 函数内部可隐藏外部变量(避免污染全局);
  • 模拟块作用域的经典技巧:IIFE(立即执行函数表达式)
(function() {
  var a = 1; // 仅在 IIFE 内部可见
})();
console.log(a); // ReferenceError

2. 块作用域:ES6 的革命性补充

  • let / const 引入真正的块级作用域{} 内);
  • 常用于 ifforwhile 等语句块中;
  • 解决 var 在循环中闭包的经典问题:
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 输出 0,1,2(正确!)
}
// 若用 var,则输出 3,3,3

3. 其他块作用域形式

  • try...catch 中的 catch 参数具有块作用域;
  • with(已废弃)也曾创建作用域,但因性能与安全问题不推荐使用。

二、变量与函数提升(第 4 章)

1. 提升的本质

  • JavaScript 不存在“先声明后使用”的运行时要求
  • 引擎在编译阶段将声明(varfunction)“移动”到作用域顶部;
  • 只有声明被提升,赋值/执行留在原地

2. var 提升示例

console.log(a); // undefined(不是报错!)
var a = 2;
// 实际等价于:
var a;
console.log(a); // undefined
a = 2;

3. 函数提升优先级更高

  • 函数声明(function foo() {})会被完整提升(包括函数体);
  • 函数表达式(var foo = function() {})仅提升变量名,值为 undefined
foo(); // ✅ 正常执行
bar(); // ❌ TypeError: bar is not a function

function foo() { console.log('声明式'); }
var bar = function() { console.log('表达式'); };

4. 函数与变量同名?函数胜出!

foo(); // "1"
var foo;
function foo() { console.log("1"); }
foo = function() { console.log("2"); };
// 编译后:function foo 被提升,覆盖 var foo

⚠️ 注意:在同一个作用域内重复 var 声明会被忽略,但重复 function 声明会覆盖。


三、关键结论

  • 作用域决定变量可见性,提升决定变量何时可用
  • 优先使用 let/const 避免 var 的提升陷阱;
  • 函数声明提升 > 变量提升;
  • 理解“编译阶段 vs 执行阶段”是破除 JS 执行迷思的核心。

📚 建议:不要依赖提升写代码!始终在作用域顶部显式声明变量,提升可读性与可维护性。