javascript采用词法作用域,即作用域在代码编写阶段就由函数定义位置静态确定,而非运行时调用位置;变量查找沿预设的作用域链(如inner→outer→global)逐级向上,与调用栈无关,这保障了行为可预测并支撑闭包等核心特性。

JavaScript 中的作用域,核心就是变量和函数“能被谁看到、在哪里能用”。词法作用域(Lexical Scope)不是运行时才决定的,而是在你写完代码、还没执行之前,就由代码的物理结构——也就是函数和块怎么写的、嵌套在哪一层——固定下来了。
词法作用域看的是“写在哪”,不是“在哪调”
函数内部能访问哪些变量,取决于它在源码中定义的位置,跟它后来被谁调用、从哪调用完全无关。
- 比如一个函数在 全局里定义,那它的词法作用域链顶端就是全局作用域;
- 如果它写在另一个函数 内部,那它天然带上外层函数的作用域,无论这个内层函数是立刻执行、返回出去、还是传给 setTimeout;
- 哪怕把函数赋值给变量、作为参数传到别的文件里,它的词法作用域也不会变。
作用域链在词法分析阶段就搭好了
JS 引擎执行前会先做词法分析,把代码拆成 token,同时根据大括号、function 关键字这些语法结构,一层层构建出作用域的嵌套关系。这个结构就像一套固定的楼梯:inner → outer → global,每级楼梯通向哪里,写完代码那一刻就定死了。
- 每次访问变量,引擎就沿着这条预设的楼梯往上找,找到第一个匹配的就停;
- 找不到就报 ReferenceError,不会跳到调用者的作用域里去找;
- 这也解释了为什么闭包能记住定义时的环境——因为那个环境本就是它词法作用域的一部分。
对比动态作用域,更能看清词法的特点
动态作用域的语言(比如某些 shell 脚本)会在函数调用时临时拼接作用域链,按调用栈回溯找变量。但 JavaScript 不这样:
- let x = "global"; function bar() { console.log(x); } function foo() { let x = "local"; bar(); } foo(); —— 输出 "global",因为 bar 定义在全局,它的词法作用域链里没有 foo;
- 如果 JS 是动态作用域,这里就会输出 "local",但它不是。
词法作用域让行为可预测,调试有依据,也支撑了闭包、模块封装、高阶函数等关键特性。它不是运行规则,而是书写契约。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











