JavaScript作用域链是函数创建时绑定的静态词法环境嵌套链,变量查找按定义时的作用域链从当前环境向上逐级搜索直至全局,未声明则报ReferenceError。
JavaScript 作用域链的本质,是函数定义时所处词法环境的嵌套关系形
成的查找路径。变量查找遵循“就近原则 + 沿作用域链向上逐级搜索”的规则,直到全局作用域;找不到就报 ReferenceError。
不是执行时决定,而是函数对象被创建(即函数声明或表达式求值)的那一刻,引擎会把当前词法环境(Lexical Environment)记录在函数的内部属性 [[Environment]] 中。这个属性指向一个环境记录(Environment Record),并带有对外层环境的引用——这就是链的起点。
[[Environment]] 指向全局环境[[Environment]] 就指向外层函数的词法环境每次访问一个标识符(如 foo),JS 引擎会按以下顺序查找:
let/const/function)outer 引用(即 [[Environment]] 所指环境)继续查ReferenceError
let/const 有暂时性死区,但仍是“已声明”)它们都走同一套作用域链机制,但声明提升(hoisting)影响初始状态:
var 和 function 声明会被提升到作用域顶部,初始化为 undefined(function 还会提升函数体)let/const 虽然也进入词法环境,但在声明语句执行前不可访问(TDZ),此时访问会报 ReferenceError,不是 undefined
很多人误以为“谁调用了我,我就往谁的作用域里找”,这是错的。作用域链由函数**定义的位置**决定,跟调用位置无关。
setTimeout 里的回调能访问外层变量,哪怕外层函数早已执行完毕基本上就这些。理解作用域链的关键,是抓住“定义时绑定”和“单向向上查找”两个核心。不复杂,但容易忽略细节。