javascript词法作用域的核心是:变量生效范围由函数定义位置决定,而非调用位置;编译时即确定嵌套层级与作用域链,查找变量时逐级向上(自身→外层函数→全局),严格遵循代码书写结构,与调用栈无关。

JavaScript 中词法作用域决定函数变量生效范围的方式,核心就一句话:**看函数写在哪,而不是看函数在哪被调用**。这个“位置”是代码写完、还没运行时就固定下来的,编译器在词法分析阶段就画好了变量能被访问的边界。
变量生效范围由函数定义位置决定
函数内部能访问哪些变量,取决于它在源码中嵌套的层级结构。比如一个函数写在另一个函数里面,它天然就有权访问外层函数里声明的变量,哪怕外层函数早已执行完毕。
- 全局作用域里声明的变量,所有地方都能访问(除非被遮蔽)
- 函数 A 内部声明的变量,只对 A 及其内部嵌套的函数生效
- 函数 B 写在函数 A 里面,B 就自动加入 A 的作用域链,能读取 A 的变量
查找变量走的是“作用域链”,不是调用栈
当函数里用到一个变量,JS 引擎不会看“此刻是谁调用了我”,而是按函数定义时的嵌套关系,一层层往外找:
- 先查自己函数内部有没有这个变量(包括参数和 let/const/var 声明)
- 没有就查上一级函数作用域(也就是它被写进去的那个函数)
- 再没有就继续往上,直到全局作用域
- 全程不关心函数是在哪被调用的——哪怕从完全不同的上下文调用,结果也一样
块级作用域也遵循词法规则
ES6 引入的 let 和 const 不仅限于函数,还支持块级作用域(如 if、for、{}),但它们依然遵守词法作用域原则:
- 在哪个块里声明,就只在那个块及内部嵌套的块中有效
- if 块里用 let 声明的变量,不会泄露到外层函数中
- 这种“块”的边界也是写代码时就确定的,运行时不会因为条件真假而改变
和动态作用域有本质区别
有些语言(比如老式 Shell 脚本)采用动态作用域:函数访问变量时,会顺着调用链往回找。但 JavaScript 不是这样:
- 即使函数被传到别的地方执行,它仍按原始定义位置找变量
- 典型例子:inner 函数定义在 outer 里,无论 outer 是不是刚被调用,也无论 inner 是不是在 another 函数里被调用,它始终访问 outer 里的变量
- 这就是为什么闭包能“记住”定义时的环境——环境是写死在代码结构里的
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











