javascript 执行环境和作用域

420 阅读5分钟

javascript 执行环境和作用域

当JavaScript代码执行的时候,会进入不同的执行环境(也可以理解为作用域),

执行环境定义了变量或函数有权访问的其他数据,这些执行环境会构成了一个执行环境栈(Execution context stack,ECS 可以理解为作用域链)。执行环境都有一个与之关联的变量对象 variable object。环境中的定义的所有变量和函数都保存在这个对象里。

某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出)

在浏览器中,全局执行环境被认为是window对象,因此所有的全局变量和函数都是作为window对象的属性和方法。

每个函数都有自己的执行环境,当执行流进入函数时,函数的环境就会被推入一个环境栈中,而在函数执行后,栈将这个执行环境弹出,把控制权返回给之前的执行环境。

当代码在一个环境执行时,会创建变量对象的一个作用域链 scope chain 。其用途是保证对执行环境有权访问所有变量和函数的有序访问。

var color = 'red';
function changeColor(){
    var localColor = 'green';
    function swapColor(){
        var tmpColor = localColor;
        localColor = color;
        color = tmpColor;
    }
    swapColor();
}
changeColor();
console.log(color);// green

代码涉及3个运行环境:
1. 全局环境
    |- 变量color
    |- 函数changeColor()
2. changeColor()局部环境:可访问全局环境中的变量
    |- 变量localColor
    |- 函数swapColor()
3. swapColor() 局部环境
    |- 变量 tmpColor:当前环境中才能访问,其他环境无权访问。

作用域链结构
window 全局环境
  |- color
  |- changeColor() 局部环境
    |- localColor
    |- swapColor() 局部环境
      |- tmpColor

swapColor()内部可访问其他两个环境中的所有变量,因为它们是它的父执行环境。

对于swapColor()而言,其作用域链上包含3个对象:
|- swapColor()的变量对象
|- changeColor()的变量对象
|- 全局变量对象

swapColor()的局部环境开始时会先在自己的变量对象中搜索变量和函数名,如果搜索不到则搜索上一级作用域。
changeColor()的作用域链中只包含两个对象:自己的变量对象和全局对象,所以它不能访问swapColor()的环境。

执行环境组成

当JS代码执行时,就会进入不同的执行环境,每个执行环境由三部分构成:

  1. 变量对象(VO, variable object):变量对象即包含变量的对象,开发者无法直接访问。
  2. 作用域属性:[[Scope]]属性时一个指向单向链表的头结点的指针作用域即变量对象,作用域链是一个由变量对象组成的带头结点的单向链表,作用是用来进行变量查找。
  3. this:指向一个环境对象

执行环境和作用域是完全不同的概念。从根本上来讲,作用域是基于函数的,而执行环境是基于对象的。换句话说,作用域涉及到被调用函数中的变量访问,而且不同调用场景是不一样的。执行环境始终是this关键字的值,它是拥有当前所执行代码的对象的引用。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然编码中无法访问这个对象,但解析器在处理数据时会在后台使用它。


执行环境分类

全局执行环境 在浏览器环境中全局执行环境就是window对象,window对象是JS代码开始运行时的默认环境。全局执行环境的变量对象始终都是作用域中的最后一个对象。

函数执行环境 当某函数被调用时,首先会创建一个执行环境及相应的作用域链,然后使用arguments和其他命名参数的值来初始化执行环境的变量对象。

当Web页面中第一次载入JS代码时,会创建一个全局执行环境(window对象)所有全局变量和函数都作为window对象的属性和方法而创建。


执行环境的生命周期

当JS代码执行时,JS解释器会通过两个阶段产生一个执行环境:

  1. 创建阶段:当函数被调用时,尚未执行函数内部代码之前。
  • 创建变量对象
  • 设置作用域属性的值
  • 设置this的值
  1. 激活阶段:代码执行阶段
  • 初始化变量对象:设置变量的值、函数的引用
  • 解释并执行代码

创建变量对象

创建变量对象的流程如下

  1. 根据函数的参数,创建并初始化arguments对象
  2. 声明提升 2.1. 扫描函数内部代码,查找函数声明。 查找所有函数声明将函数名和函数引用存入变量对象中,如果变量对象中已存在同名函数则覆盖。 2.2. 扫描函数内部代码查找变量声明 查找所有变量声明,将变量名存入变量对象中,初始化为undefined。如果变量名和已声明的形参或函数同名,则什么也不做。

访问标识符

当执行JS代码时遇到标识符,就会根据标识符的名称,在运行时上下文的作用域链中进行搜索。从作用域的第一个对象开始(如函数的活动对象)查找,如果没有找到,就搜索作用域链中下一个对象,如此往复直到找到标识符的定义。如果在搜索完作用域中的最后一个对象(全局对象)以后也没有找到,则会抛出一个错误,提示undefined

2020_07_22_HR1yA3
2020_07_22_HR1yA3
资料参考来源:

[红宝书]

JS执行环境

本文使用 mdnice 排版

文章列表

JavaScript 基本概念

JavaScript 数据类型

JavaScript 基本类型和引用类型

javascript 执行环境和作用域

javascript 垃圾收集机制

javascript 数组