JavaScript变量查找严格遵循词法作用域:从函数定义处的词法环境开始,沿作用域链逐级向上(自身→外层→全局),不跳过、不跨层;var可访问未初始化值,let/const遇TDZ即报错;闭包固化定义时的作用域链。

分析 JavaScript 作用域面试题中的变量查找路径,核心是还原引擎“从哪开始找、往哪去找、什么时候停”的过程。它不依赖直觉或调用位置,而严格遵循词法作用域 + 作用域链的静态规则。
看函数定义位置,定作用域链起点
变量查找永远从**当前函数定义时所处的词法环境**开始构建作用域链,不是调用时的位置。比如:
function print() { console.log(x); }
function fn() {
let x = 'inner';
print(); // 这里调用,但 print 定义在全局
}
let x = 'global';
fn(); // 输出 'global',不是 'inner'
因为 print 函数定义在全局作用域,它的作用域链只有:print 自身(空)→ 全局。它根本“看不见” fn 内部的 x。
按嵌套层级逐级向上查,不跳不跨
查找路径是严格线性的:当前作用域 → 直接外层函数作用域 → 再外层 → … → 全局。不能跳过某一层,也不能横向去兄弟作用域找。
例如:
let a = 'global';
function outer() {
let a = 'outer';
function inner() {
let a = 'inner';
console.log(a); // 找自己:'inner'
console.log(b); // 假设 b 未声明:查 inner → outer → 全局 → 报错
}
inner();
}
outer();
查 b 的路径就是:inner(无)→ outer(无)→ 全局(无)→ ReferenceError。
区分 var/let/const,注意提升与死区
查找行为本身不受声明方式影响,但能否“找到”以及“找到时是否能用”,取决于声明是否存在、是否已初始化:
-
var:声明被提升,变量存在但值为
undefined,可访问(不报错); -
let/const:声明也被提升,但处于“暂时性死区(TDZ)”,在初始化前访问会直接报
ReferenceError; - 查找路径相同,但走到某一层时,遇到 TDZ 就立刻中断并报错,不会继续往上找。
例如:function test() { console.log(a); let a = 1; } —— 查找 a 时,在当前作用域就撞上 TDZ,直接报错,根本不会去查外层或全局。
闭包题关键:确认函数“记住”的是哪条链
闭包的本质是函数对象携带了它定义时的作用域链快照。分析时要锁定两点:
- 这个函数是在哪个作用域里写的?
- 它引用的变量在当时属于哪一层作用域?
典型例子:
function makeAdder(x) {
return function(y) { return x + y; }; // 这个函数记住了 makeAdder 的参数 x
}
const add5 = makeAdder(5);
add5(3); // 8 —— x 是 makeAdder 调用时生成的那个作用域里的 x,不是全局的 x
所以查找 x 的路径是:内部函数自身(无)→ makeAdder 的作用域(有)→ 停止。这条链被闭包“冻结”了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











