当JavaScript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)。
对于每个执行上下文,都有三个重要属性:
- 变量对象(Variable object,VO)
- 作用域链(Scope chain)
- this
前面已经讲解了this,今天来讲讲作用域及作用域链。
作用域
作用域是指程序源代码中定义变量的区域。
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
所谓的词法作用域,就是代码在编写过程就体现出来的作用范围。代码一旦写好,不用执行, 作用范围就已经确定好了,这个就是所谓的词法作用域。
词法作用域的规则:
- 函数允许访问函数外的数据 (也有就近原则)
- 整个代码结构中只有函数可以限定作用域
- 作用域内首先使用变量提升分析
- 如果当前作用域找到所需变量,则停止查找
*与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
分析下执行过程:
执行 foo 函数,先从 foo 函数内部查找是否有局部变量 value,如果没有,就根据书写的位置,查找上面一层的代码,也就是 value 等于 1,所以结果会打印 1。
假设JavaScript采用动态作用域,让我们分析下执行过程:
执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。
看一个《JavaScript权威指南》中的例子:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();
两段代码都会打印:local scope
。因为JavaScript采用的是词法作用域,函数的作用域基于函数创建的位置。
引用《JavaScript权威指南》的回答就是:
JavaScript 函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。嵌套的函数 f() 定义在这个作用域链里,其中的变量 scope 一定是局部变量,不管何时何地执行函数 f(),这种绑定在执行 f() 时依然有效。
作用域链
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
这里不具体介绍函数的[[scope]]属性,只介绍一下怎么简单的分析作用域链。
作用域链绘制规则如下:
- 全局变量,函数声明都是属于0级链,每个对象占一个位置
- 遇到函数声明就延伸一个链出来,一级级展开
- 访问变量时首先从当前函数内部查找,如果当前作用域没有定义,往上级链中检查
- 如此往复,直到0级链,如果0级没有,则这个变量为undefined
以代码为例:
function func1(){
alert(num);
}
function func2(){
var num=456;
function func3(){
func1();
}
func3();
}
func2();//结果显示:num is not defined
上述代码作用域链如下:

分析下代码执行流程:
- 当程序执行到func2时,进入func2的执行环境
- 当程序执行到func3时,在func3上下文中没有找到func1
- 回到1级作用域,即func3声明位置所在的1级作用域寻找,没有找到
- 回到func2声明时所在0级作用域寻找,找到后进入func1的上下文环境
- func1归属于0级作用域,内部只能访问func1内部作用域及0级作用域,在func1作用域内没有找到num,回到0级作用域,也没有找到,抛出错误
请注意:
- 只有函数声明会延伸下级链,调用只会查找,不会展开下级链
- 所有链都能访问到0级链,0级链只能访问0级链
相关系列: 从零开始的前端筑基之旅(超级精细,持续更新~)
如果你收获了新知识,就给作者点个赞吧~他急需支持~

参考文章:
- ****JavaScript深入之词法作用域和动态作用域****
- ****JavaScript深入之作用域链****
- ****JS-词法作用域 作用域链****