定义
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境(执行上下文)有权访问的所有变量和函数的有序访问。
它有两个对象
- 变量对象(VO):变量对象即包含变量的对象,变量对象我们无法访问,除此之外和普通对象没什么区别。变量对象存储了在上下文中定义的变量和函数声明
- 活动对象(AO):是在进入函数执行环境时刻被创建的,它通过函数的 arguments 属性初始化。
变量对象和活动对象的关系
未进入执行阶段之前,变量对象(VO)中的属性都不能访问,只是声明但是进入执行阶段之后,变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。它们其实都是同一个对象,只是处于执行环境的不同生命周期。AO 实际上是包含了 VO 的。因为除了 VO 之外,AO 还包含函数的 parameters,以及 arguments 这个特殊对象。也就是说 AO 的确是在进入到执行阶段的时候被激活,但是激活的除了 VO 之外,还包括函数执行时传入的参数
和 arguments 这个特殊对象。
- 查看当前作用域,如果当前作用域声明了这个变量,可以直接访问
- 查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明,有就返回变量,没有继续下一步
- 再查找上级函数的上级函数,直到全局作用域为止,有则返回,无则继续
- 如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)
那么让我们来看看几个例子
- 例子一
function fun(num){
var age = 20;
num();
}
fun(function(){
console.log(age);//报错
//1.在当前作用域没有查找到age
//2.查找上一级作用域:全局作用域
//为何是全局作用域?因为看上一级作用域,不是看函数在哪调用,而是看函数在哪编写的。
//这种特别的作用域,叫做“词法作用域”
})
现在我们来分析下作用域链如何查找变量的:
- console.log(age)的时候,在当前作用域并没有查询到age变量。所以查找上一级作用域。
- 上级作用域是谁?这里需要引出一个概念,查找函数上级作用域,不是看函数在哪调用,而是看函数在哪编写。所以这样来看,上级作用域就是全局作用域。
- 在全局作用域中并没有声明age变量,所以console.log(age);就会报错。
- 例子二
var b = 100;
function fn(){
var a = 100
function fun1(){
console.log(c);//undefined
a += 10;
var c = 10;
function fun2(){
}
fun2();
}
console.log(a);//100
fun1();
console.log(a);//110
console.log(b);//100
}
fn();
作用域链:有多级作用域连续引用新城的链式结构,管控一切变量的使用顺序
先在自己的AO中找,就延作用域链向父级作用域中去找
GO{
a : undefined,
fn : function ...
}
fnAO{
b : 10,
fun1: function...,
}
fun1AO{
c : 20;
}
当前作用域查到了变量,则不会再继续寻找,直接返回该变量的值,这里打印的时候变量声明但是未赋值,所以c输出undefined。