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代码执行时,就会进入不同的执行环境,每个执行环境由三部分构成:
- 「变量对象」(VO, variable object):变量对象即包含变量的对象,开发者无法直接访问。
- 「作用域属性」:[[Scope]]属性时一个指向单向链表的头结点的指针作用域即变量对象,作用域链是一个由变量对象组成的带头结点的单向链表,作用是用来进行变量查找。
this:指向一个环境对象执行环境和作用域是完全不同的概念。从根本上来讲,作用域是基于函数的,而执行环境是基于对象的。换句话说,作用域涉及到被调用函数中的变量访问,而且不同调用场景是不一样的。执行环境始终是
❞this关键字的值,它是拥有当前所执行代码的对象的引用。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然编码中无法访问这个对象,但解析器在处理数据时会在后台使用它。
执行环境分类
❝「全局执行环境」 在浏览器环境中全局执行环境就是
window对象,window对象是JS代码开始运行时的默认环境。全局执行环境的变量对象始终都是作用域中的最后一个对象。「函数执行环境」 当某函数被调用时,首先会创建一个执行环境及相应的作用域链,然后使用
arguments和其他命名参数的值来初始化执行环境的变量对象。当Web页面中「第一次载入JS代码」时,会创建一个「全局执行环境(window对象)」,「所有全局变量和函数都作为
❞window对象的属性和方法」而创建。
执行环境的生命周期
❝当JS代码执行时,JS解释器会通过两个阶段产生一个执行环境:
- 「创建阶段」:当函数被调用时,尚未执行函数内部代码之前。
- 创建变量对象
- 设置作用域属性的值
- 设置
this的值
- 「激活阶段」:代码执行阶段
❞
- 初始化变量对象:设置变量的值、函数的引用
- 解释并执行代码
创建变量对象
❝创建变量对象的流程如下
❞
- 「根据函数的参数,创建并初始化
arguments对象」。- 「声明提升」 2.1. 扫描函数内部代码,查找「函数声明」。 查找所有函数声明将函数名和函数引用存入变量对象中,如果变量对象中已存在同名函数则覆盖。 2.2. 扫描函数内部代码查找「变量声明」 查找所有变量声明,将变量名存入变量对象中,初始化为
undefined。如果变量名和已声明的形参或函数同名,则什么也不做。
访问标识符
❝当执行JS代码时遇到标识符,就会根据标识符的名称,在运行时上下文的作用域链中进行搜索。从作用域的第一个对象开始(如函数的活动对象)查找,如果没有找到,就搜索作用域链中下一个对象,如此往复直到找到标识符的定义。如果在搜索完作用域中的最后一个对象(全局对象)以后也没有找到,则会抛出一个错误,提示
undefined。❞ 2020_07_22_HR1yA3
资料参考来源:
[红宝书]
本文使用 mdnice 排版
