每当控制器到达ECMAScript可执行代码的时候,控制器就进入了一个执行上下文.
执行上下文是个抽象概念,标准中没有从技术实现上定义执行上下文的具体结构和类型.
就是一系列活动的执行上下文从逻辑上形成一个栈(比较抽象).
栈底总是全局上下文,栈顶是当前(活动的)执行上下文.
当在不同的执行上下文间切换(退出而进入新的执行上下文)的时候,栈会被修改(通过压栈或者出栈的形式).
变量对象
执行上下文的数据是以变量对象的属性形式进行存储的.
一个变量对象(简写为VO)是一个和执行上下文相关的特别对象,存储以下内容:
变量(声明的变量,var)
函数声明(简写为FD)
在上下文中,函数声明的形式参数
作用域链
作用域链是一条变量对象的链,它和执行上下文有关,用于在处理标识符的时候进行变量查询.
函数上下文的作用域链在函数调用的时候创建出来,它包含了活跃对象和该函数的内部[[Scope]]属性.
执行上下文变量大致如下:
activeExecutionContext = {
VO:{...},//或者AO
this:thisValue,
Scope:[
//作用域链,所有变量对象的列表,用来查询标识符
]
}
上面Scope可以定义如下:
Scope = AO+[[Scope]]
可以用数组进行表示:
var Scope = [VO1,VO2,...,VOn]//作用域链
执行环境(execution context,为简单起见,有时也称为“环境”)是 JavaScript 中最为重要的一个概 念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个 与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。虽然我们 编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。 全局执行环境是最外围的一个执行环境。根据 ECMAScript 实现所在的宿主环境不同,表示执行环 境的对象也不一样。在 Web 浏览器中,全局执行环境被认为是 window 对象(第 7 章将详细讨论),因 此所有全局变量和函数都是作为 window 对象的属性和方法创建的。某个执行环境中的所有代码执行完 毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退 出——例如关闭网页或浏览器——时才会被销毁)。 每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。ECMAScript 程序中的执行流 正是由这个方便的机制控制着。 当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是 保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所 在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对 象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中 的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延 续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。首先说一下js全局变量的作用域:
在js中如果直接定义一个变量,都是在window这个作用域下定义的
比如
var num = 60
也可以写成这样
window.num = 60
这两个写法是完全一样的,不管哪种写法,使用 console.log(num, window.num) 都可以打印出来 num的值
相对于题主的这个例子:
函数内部声明一个局部变量需要 加 var关键字,如果没有 var,根据向上查找原则,就会去方法 f1 上层去查找 num 变量,如果有,直接拿来用,如果没有就会继续再向上层查找,如果到了 window域 下还没找到,这时 就会在 window域 下创建一个 num变量
所以这个例子打印结果是 60
再说这一个例子
f1 中使用 num变量,在方法形参中 已经有一个 num
实际上,在js加载过程中,编译器在编译过程中已经在 f1 中对 num变量 进行了声明,也就是说 num 已经是 f1 作用域下的一个变量了,所以,在函数内再使用 num的时候,就是在使用 f1作用域下的 num变量,而不会再向上层查找