JavaScript作用域由函数声明、let/const块级绑定及执行上下文嵌套共同决定;var具函数作用域和提升,let/const具块作用域和暂时性死区;this和arguments动态绑定,箭头函数继承外层this。

JavaScript 作用域不是“变量在哪定义就只能在哪用”这么简单,而是由函数声明、let/const 块级绑定、以及执行上下文的嵌套关系共同决定的。变量能不能访问,关键看「当前执行位置」是否处于该变量的词法作用域内。
函数作用域 vs 块作用域:var 和 let/const 的行为差异
var 声明的变量只有函数作用域(或全局作用域),且存在变量提升(hoisting);let 和 const 是块级作用域(如 {}、if、for 内部),不提升,且有暂时性死区(TDZ)。
-
var x = 1在if块里声明,外部仍可访问;let y = 2在同一位置声明,外部访问会报ReferenceError: y is not defined -
console.log(a)在var a = 3前执行,输出undefined(不是报错);但console.log(b)在let b = 4前执行,直接抛ReferenceError - 循环中用
var声明的计数器(如for (var i = 0; i ),i在循环结束后依然存在且值为3;用let i则循环外访问i会报错
function 声明和 const fn = function() {} 的作用域区别
函数声明(function foo() {})会被完全提升,包括函数体;而函数表达式(const foo = function() {})遵循 const 规则——仅声明被提升,赋值不提升,且受块作用域限制。
-
foo()在function foo() { return 'a'; }之前调用,能正常执行 -
bar()在const bar = function() { return 'b'; }之前调用,报TypeError: bar is not a function(因为bar已声明但尚未赋值,处于 TDZ) - 在
if (false) { function baz() {} }中,非严格模式下baz仍可能被提升到函数作用域顶层(浏览器兼容性行为),但这是不推荐依赖的边缘情况
闭包如何“捕获”外层作用域变量
闭包不是特殊语法,而是函数对象与其创建时词法作用域的组合。只要内部函数在外部被调用,它就能持续访问其定义时所在作用域中的变量,哪怕外层函数已执行完毕。
立即学习“Java免费学习笔记(深入)”;
-
function outer() { let count = 0; return function inner() { count++; return count; }; }—— 每次调用outer()都生成一个独立的count绑定,inner闭包持有对它的引用 - 常见陷阱:循环中用
var创建函数,所有函数共享同一个变量;改用let或显式闭包(如(i => () => i)(i))才能隔离每次迭代的值 -
this、arguments不参与闭包捕获,它们是每次调用时动态绑定的;箭头函数则不绑定自己的this,而是继承外层函数的this
真正容易出问题的不是“作用域是什么”,而是你没意识到某个 if 块、某个回调函数、甚至某个异步 setTimeout 回调,已经切换了词法作用域边界。尤其在重构老代码(大量 var)或调试闭包内存泄漏时,得一层层查变量声明的位置和方式,而不是只看缩进或运行时调用栈。











