什么是javascript作用域_变量访问规则是什么【教程】

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

JavaScript 作用域不是“变量在哪定义就只能在哪用”这么简单,而是由函数声明、let/const 块级绑定、以及执行上下文的嵌套关系共同决定的。变量能不能访问,关键看「当前执行位置」是否处于该变量的词法作用域内。

函数作用域 vs 块作用域:varlet/const 的行为差异

var 声明的变量只有函数作用域(或全局作用域),且存在变量提升(hoisting);letconst 是块级作用域(如 {}iffor 内部),不提升,且有暂时性死区(TDZ)。

  • var x = 1if 块里声明,外部仍可访问;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 仍可能被提升到函数作用域顶层(浏览器兼容性行为),但这是不推荐依赖的边缘情况

闭包如何“捕获”外层作用域变量

闭包不是特殊语法,而是函数对象与其创建时词法作用域的组合。只要内部函数在外部被调用,它就能持续访问其定义时所在作用域中的变量,哪怕外层函数已执行完毕。

  • function outer() { let count = 0; return function inner() { count++; return count; }; } —— 每次调用 outer() 都生成一个独立的 count 绑定,inner 闭包持有对它的引用
  • 常见陷阱:循环中用 var 创建函数,所有函数共享同一个变量;改用 let 或显式闭包(如 (i => () => i)(i))才能隔离每次迭代的值
  • thisarguments 不参与闭包捕获,它们是每次调用时动态绑定的;箭头函数则不绑定自己的 this,而是继承外层函数的 this

真正容易出问题的不是“作用域是什么”,而是你没意识到某个 if 块、某个回调函数、甚至某个异步 setTimeout 回调,已经切换了词法作用域边界。尤其在重构老代码(大量 var)或调试闭包内存泄漏时,得一层层查变量声明的位置和方式,而不是只看缩进或运行时调用栈。