作用域:代码中定义变量的区域,确定执行代码访问变量的权限。
JS采用词法作用域(静态作用域),所以函数的作用域在函数定义的时候就决定了,也就是说函数的作用域基于函数创建的位置。相对的是动态作用域,函数的作用域在函数调用的时候决定。
作用域分类:全局作用域、函数作用域和eval()作用域(不讨论)
JS代码的整个执行过程,分为两个阶段:代码编译阶段和代码执行阶段。编译阶段由编译器完成,将代码翻译成可执行代码。执行阶段由js引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。
执行上下文
当JS执行到一段可执行代码的时候就会创建对应的执行上下文,JS引擎创建了执行上下文栈(ECS)管理执行上下文。执行上下文有三个重要属性:变量对象(VO)、作用域链(Scope Chain)、this。
执行上下文的生命周期可以分为两个阶段:创建阶段和代码执行阶段。
创建上下文阶段
在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向。
变量对象(VO)
与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。
- 全局上下文中的变量对象初始化是全局对象
- 函数上下文中的变量对象(VO只包括Arguments对象
- 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始化的属性值,这个时候VO会被激活为AO
- 代码执行阶段,会再次修改变量对象的属性值
>变量对象的创建
- 建立arguments对象,检查当前上下文中的参数,建立该对象下的属性与属性值
- 检查当前上下文中的函数声明,在VO中以函数名建立一个属性,函数所在内存地址的引用为属性值。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖
- 检查当前的变量声明,在VO中以变量名建立一个属性名,属性值为undefined。如果该变量名已经存在,为了防止同名函数被修改为undefined,会直接跳过该变量,原属性值不会被修改
代码执行阶段
顺序执行代码,根据代码,修改变量对象的值
总结完了那看两个代码吧~
1 | console.log(foo); |
1 | // 首先,创建上下文阶段: |
1 | function foo() { |
1 | // 首先,创建上下文阶段: |