javascript作用域链是代码在执行过程中用于查找变量的一个链式结构,它由当前执行上下文的作用域和所有外层作用域按照嵌套关系依次组合而成,决定了变量和函数的可访问范围。

什么是作用域链
要理解作用域链,首先需要明确两个基础概念:词法作用域和执行上下文。
词法作用域也叫静态作用域,指的是变量和函数的作用域在代码编写阶段就已经确定,和代码执行的位置无关,只和函数、变量声明的嵌套位置有关。
执行上下文是javascript代码执行时的环境,每次函数调用或者全局代码执行都会创建一个执行上下文,每个执行上下文内部都有一个对应的作用域链。
作用域链的本质是一个指向各个作用域对象的指针列表,这些作用域对象按照从内到外的嵌套顺序排列,当前执行上下文对应的作用域排在链的最前端,外层嵌套的作用域依次排在后面,最末端是全局作用域。
作用域链的形成过程
当javascript引擎执行一段代码时,会先创建对应的执行上下文,在创建执行上下文的阶段就会同步生成作用域链,具体过程如下:
- 首先获取当前代码的词法作用域,也就是当前代码所在的作用域对象,将其作为作用域链的第一个元素
- 然后向上查找外层嵌套的作用域,将外层作用域对象依次加入作用域链
- 重复向上查找的过程,直到找到全局作用域,全局作用域会成为作用域链的最后一个元素
我们可以通过一个简单的嵌套函数示例来理解这个过程:
// 全局作用域
var globalVar = '全局变量';
function outer() {
var outerVar = '外层函数变量';
function inner() {
var innerVar = '内层函数变量';
console.log(innerVar); // 访问自身作用域的变量
}
inner();
}
outer();
上面的代码中,inner函数执行时创建的执行上下文,其作用域链会包含三个部分:首先是inner函数自身的作用域,然后是outer函数的作用域,最后是全局作用域。
变量是如何通过作用域链查找的
当我们在代码中使用一个变量时,javascript引擎会沿着当前执行上下文的作用域链,从最前端开始依次查找该变量,具体规则如下:
- 首先在当前作用域(作用域链的第一个元素)中查找该变量,如果找到就直接使用,停止查找
- 如果当前作用域没有找到,就沿着作用域链向上,到下一个外层作用域中查找
- 重复这个过程,直到遍历完整个作用域链
- 如果遍历完所有作用域都没有找到该变量,就会抛出引用错误
ReferenceError
我们结合下面的代码示例来看具体的查找过程:
var a = 10;
function fn1() {
var b = 20;
function fn2() {
var c = 30;
console.log(c); // 第一步:当前fn2作用域找到c,输出30
console.log(b); // 第二步:fn2作用域没找到b,向上到fn1作用域找到b,输出20
console.log(a); // 第三步:fn2、fn1作用域都没找到a,向上到全局作用域找到a,输出10
console.log(d); // 第四步:所有作用域都没找到d,抛出ReferenceError
}
fn2();
}
fn1();
上面的代码中,fn2执行时的作用域链是[fn2作用域, fn1作用域, 全局作用域],所以查找变量时会按照这个顺序依次匹配。
作用域链的常见注意事项
1. 作用域链是静态的
因为词法作用域在代码编写时就确定了,所以作用域链也是在代码定义时就确定的,不会因为执行位置的改变而改变。比如下面的例子:
var x = '全局x';
function foo() {
console.log(x);
}
function bar() {
var x = 'bar里的x';
foo(); // 输出"全局x",而不是"bar里的x"
}
bar();
foo函数的作用域链在定义时就已经确定包含全局作用域,所以执行时不管在哪里调用,查找x都会先找自身作用域,再找全局作用域,不会受到调用位置bar函数作用域的影响。
2. 闭包中的作用域链
闭包是指有权访问另一个函数作用域中变量的函数,闭包的作用域链会包含其外层函数的作用域,即使外层函数已经执行完毕,外层函数的作用域也不会被销毁,因为闭包的作用域链还在引用它。示例如下:
function createCounter() {
var count = 0; // 外层函数作用域的变量
return function() {
count++;
console.log(count);
};
}
var counter = createCounter();
counter(); // 输出1
counter(); // 输出2
这里返回的内部函数就是闭包,它的作用域链包含自身作用域和createCounter的作用域,所以即使createCounter执行完了,count变量还是可以通过作用域链被访问到,不会被垃圾回收。
3. 变量提升对作用域链查找的影响
变量提升会让变量声明提前到当前作用域的顶部,但是赋值操作还是在原来的位置,所以在赋值之前访问变量会得到undefined,而不是沿着作用域链向上查找。示例如下:
var m = 100;
function test() {
console.log(m); // 输出undefined,而不是100
var m = 200;
}
test();
因为test函数内部声明了m,变量提升后m属于test的作用域,所以查找m时在test作用域就找到了,只是此时还没有赋值,所以是undefined,不会继续向上查找全局的m。
总结
javascript作用域链是管理变量访问的核心机制,它由当前作用域和外层所有嵌套作用域按照从内到外的顺序组成,变量查找时会沿着作用域链从前端到末端依次匹配,找到就停止,找不到就报错。作用域链是静态的,在代码定义阶段就确定了,不会随执行位置改变,理解这个机制能帮助我们避免很多变量访问相关的错误,也能更好地理解闭包等高级特性的原理。
javascript作用域链变量查找执行上下文词法作用域修改时间:2026-07-21 13:57:28