JavaScript代码是按顺序从上到下被解析的,当然JavaScript 引擎并非逐行地分析和执行代码,而是逐段地去分析和执行。当执行一段代码时,先进行预处理,如变量提升、函数提升等。
JavaScript可执行代码包括三种类型:全局代码、函数代码、eval代码。每执行一段可执行代码时,都会创建对应的执行上下文。在脚本中可能存在大量的可执行代码段,所以JavaScript引擎先创建执行上下文栈,来管理脚本中所有执行上下文。
【提示】
执行上下文是一个专业术语,比较抽象,实际上就是在内存中开辟的一块独立运行的空间,执行上下文栈相当于一个数组,数组元素就是一个个独立的执行上下文区域。
当JavaScript开始解释程序时,最先遇到的是全局代码,因此在初始化程序的时候,首先向执行上下文栈压入一个全局执行上下文,并且只有当整个应用程序结束的时候,全局执行上下文才被清空。
当执行一个函数的时候,会创建一个函数的执行上下文,并且压入到执行上下文栈,当函数执行完毕的时候,会将函数的执行上下文从栈中弹出。
每个执行上下文都有三个基本属性,本节将重点介绍变量对象。
- 变量对象
- 作用域链
- this
变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。JavaScript代码不能直接访问该对象,但是可以访问该对象的成员(如 arguments)。不同代码段中的变量对象也不相同,简单说明如下:
全局上下文的变量对象
全局上下文的变量对象,初始化是全局对象。
全局对象是预定义的对象,作为JavaScript的全局函数和全局属性的占位符。通过全局对象,可以访问所有其他所有预定义的对象、函数和属性。
在客户端JavaScript中,全局对象是Window对象,通过Window对象的window属性指向自身。
【示例1】下面代码演示了在全局作用域中声明变量b,并赋值,然后通过window对象的属性b来读取这个全局变量值。同时演示了使用this访问Window对象,使用this.window同样可以访问Window对象。
var b = true; console.log( window.b ); //true this.window.b = false; console.log(this.b); //false
函数上下文的变量对象
变量对象是ECMAScript规范术语,在一个执行上下文中,变量对象才被激活,只有激活的变量对象,它的各种属性才能被访问。
在函数执行上下文中,变量对象常常被称为活动对象,两者意思相同。活动对象是在进入函数上下文时被创建,初始化时只包括Arguments对象。它通过函数的arguments属性访问。arguments属性值为Arguments对象。
函数执行上下文的代码处理可以分成两个阶段:分析和执行,简单说明如下。
【执行过程】
第1步,进入执行上下文。当进入执行上下文时,不会执行代码,只进行分析,此时变量对象包括。
- 函数的所有形参(如果是函数上下文):由名称和对应值组成的一个变量对象的属性被创建。如果没有实参,属性值设为undefined。
- 函数声明:由名称和对应值(函数对象)组成一个变量对象的属性被创建。如果变量对象已经存在相同名称的属性,则会完全替换这个属性。
- 变量声明:由名称和对应值(undefined)组成一个变量对象的属性被创建。如果变量名称与已经声明的形参或函数相同,则变量声明不会覆盖已经存在的这类属性。
【示例1】在进入函数执行上下文时,会给变量对象添加形参、函数声明、变量声明等初始的属性值。下面代码简单演示了这个阶段的处理过程。
function f(a) { //声明外部函数 var b = 1; //声明局部变量,并赋值1 function c() {} //声明内部函数 var d = function() {}; //声明局部变量,并赋值为匿名函数 b = 2; //修改变量b的值为2 } f(3); //调用函数,并传入实参值为3
在进入函数执行上下文后,活动对象的结构模拟如下。
AO = { arguments: { 0: 3, //实参值 length: 1 //实参长度 }, a: 3, //实参值 b: undefined, //声明局部变量b c: function c(){}, //声明函数c,引用function c(){} d: undefined //声明局部变量d }
第2步,执行代码。在代码执行阶段,会按顺序执行代码,这时可能会修改变量对象的值。
【示例2】在代码执行阶段,可能会修改变量对象的属性值。针对上面示例,当代码执行完后,活动对象的结构模拟如下。
AO = { arguments: { 0: 3, //实参值 length: 1 //实参长度 }, a: 3, //实参值 b: 1, //初始化赋值 c: function c(){}, //引用声明的函数c d: function(){} //引用函数表达式"d" }