怎么理解javascript作用域链_javascript中如何查找变量

14次阅读

作用域链是javaScript中变量查找的单向链表路径,由函数定义时的词法环境决定,逐级向上查找至全局环境;闭包使外层词法环境持久化,let/const存在TDZ,eval和with会动态修改链且应避免使用。

怎么理解javascript作用域链_javascript中如何查找变量

作用域链本质是嵌套作用域的查找路径

javascript 中变量不是“全局搜索”,而是沿着当前执行上下文的作用域链一级级向上找。这条链由函数定义时的词法环境决定,和函数在哪调用无关。每次进入函数,引擎会把该函数的 [[Environment]](内部属性)连到外层作用域的 LexicalEnvironment 上,形成一条单向链表。

变量查找过程:从当前作用域开始逐级向上

查找变量时,js 引擎按以下顺序进行:

  • 先查当前执行上下文的 LexicalEnvironment(含 let/const 声明和函数参数)
  • 没找到就查其 outer 引用(即外层函数的词法环境)
  • 继续向上,直到全局环境(globalThiswindow
  • 全程找不到就抛出 ReferenceError

注意:var 声明会被提升并绑定到函数作用域(或全局),但查找逻辑仍走同一链条;let/const 有暂时性死区(TDZ),未初始化前访问会报 ReferenceError,不是“找不到”而是“不可访问”。

闭包让外层作用域持久化,改变作用域链生命周期

当一个函数返回了内部函数,且该内部函数引用了外层变量,外层词法环境不会被销毁——它被保留在返回函数的 [[Environment]] 中。这意味着作用域链在函数调用结束后依然存在。

立即学习Java免费学习笔记(深入)”;

function outer() {   const x = 42;   return function inner() {     console.log(x); // x 在 outer 的 LexicalEnvironment 中   }; } const fn = outer(); fn(); // 仍能访问 x —— outer 的作用域链被 inner 持有

常见误判点:

  • for (var i = 0; i console.log(i), 0); } 输出三个 3:因为所有回调共享同一个 var i,它属于 outer 函数作用域,循环结束时值为 3
  • 换成 let i 就输出 0,1,2:每个迭代创建独立绑定,每个回调闭包指向各自块级环境

eval 和 with 会动态修改作用域链,应避免使用

eval 在非严格模式下可向当前作用域注入变量,导致作用域链临时扩展;with 会把传入对象作为新的最内层作用域插入链首。两者都破坏静态可分析性,影响 JIT 编译优化,且极易引发意外覆盖。

例如:

function f() {   const a = 1;   with ({ a: 2 }) {     console.log(a); // 2 —— 不是从词法上确定的 a,而是运行时查 with 对象   } }

V8、SpiderMonkey 等现代引擎对含 with 或非严格 eval 的函数直接禁用优化,性能断崖式下降。ES5 起严格模式已禁止 with,也限制 eval 的作用域污染能力。

真正难理解的不是“怎么找”,而是“找谁”——取决于函数定义的位置,而不是调用的位置;而最容易被忽略的,是 let 绑定的块级环境如何被闭包捕获、以及 TDZ 如何让“存在但不可读”成为常态。

text=ZqhQzanResources