`
carge
  • 浏览: 50122 次
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

Javascript学习之一 -- 执行时的准备工工作

 
阅读更多

 

Javascript执行时的准备工作:

当JS引擎进入一个执行环境,准备执行该区域的代码时,如下工作已经完成:(1) 变量的实例化,(2) 作用域链的创建和初始化,(3) this指针所指向的目标对象的确定。

 

1、执行环境(Execution Contexts

一段可执行的JS代码即为一个执行环境,分为三类:

1.1 全局代码;全局代码是不包含任何函数体的代码(可以有函数调用);

1.2 Eval代码;eval是JS中的一个内置函数,eval的参数即全局代码;

1.3 函数代码;函数代码是函数体中的一部分代码,这部分代码不嵌套包括任何函数体代码。注意new Function(argList)最后一个参数也是当作函数代码处理。

 

 

2、变量实例化(Variable Instantiation )

 

JS的每个执行环境(也叫执行上下文)都关联到一个变量对象variable object)。变量实例化将做以下事情:

2.1  如果该执行环境是函数代码的执行环境,每一个形参作为属性添加到变量对象中,属性名为形参名,值是调用该函数时所提供的实参的值。如果实参少于形参,则多出的形参赋值为undefined。如果有形参中有两个或者两个以上的参数的名字相同,则最后一个形参的值是变量对象中该形参的值,即使最后一个形参为undefined。

 

 

2.2  把函数定义式定义的函数(不是函数表达式定义的函数)作为属性添加到变量对象中。属性名为函数名,值为通过new Function(参数列表) 创建的一个函数对象,该对象的属性由代码决定(执行的时候确定)。如果变量对象中已经存在该名字的属性,则用新的值和属性替换旧的值和属性。

   如果执行环境是函数代码,必须先初始化函数的形参列表,然后再处理该步骤。

 

 

2.3 把通过关键字var定义的变量(该变量也可以是源码某个语句块中定义的,如:for语句循环体中定义的)作为属性添加到变量对象中,属性名为变量名,值为undefined,值的属性由代码确定(执行的时候确定);如果该变量名和函数定义或者形参重合,则不摈弃变量对象中该属性的已有属性。

      语义上,必须先初始化函数形参列表和函数定义,再处理该步骤。

 

 

 

实验1(环境Chrome 8 ):

<script language="javascript">
var fun;
function fun(a, b, a){
	alert(a);
}
fun(1, 2, 3);//弹出:3
fun(1, 2);//弹出:undefined
var fun = 'not function';
fun(1);//出错
</script>

程序解读:
(1) 在执行第2行代码时,全局对象window中已经存在属性window.fun其值为fun函数,而不是一个变量,即使在函数fun前后都有对变量fun的声明,这是因为2.3所述。
(2) 第6行弹出3,第7行弹出undefined,这是因为2.1所述。
(3) 第9行出错是因为执行第8行代码时,改变了window.fun的值(第八行不是变量实例化,而是计算表达式执行代码)。

 

 

3、作用域链(Scope Chain)和变量识别(Identifier Resolution

每个执行环境都与一个作用域链关联。作用域链是一个对象列表,主要用于变量识别(可以理解为它由赋值后的变量对象以链表的形式构成)。系统按照以下步骤来计算一个变量 v的值:

step 1:  获取作用域链中的下一个对象 ob,如果不存在下一个则转step 3;
step 2:  调用ob.hasProperty(v),如果结果为true,返回ob.v的引用;如果为false,则转到step 1;
step 3:  否则返回一个null.v (null是Null的一个对象).

注意:用到作用域链时,说明代码已经开始执行,这时作用域链中各个对象的值已计算得到,例如:变量对象中未确定的属性的值。

4、全局变量(global object)

 

在JS运行环境中存在一个特殊的全局对象,该对象在执行任何代码之前创建。在HTML文档对象模型中全局对象为DOMWindow,DOMWindow有一个属性window,用来实现对它自身的引用。故可把window视为全局对象。全局对象中有一些JS语言以外的对象(它们不是由JS语言实现),如Date,Math等。执行代码时,可改变全局对象中的属性,如添加、删除、修改属性等。

 

 

5、活动对象(activation object)

当JS执行引擎跳转到一个函数代码执行环境时,将创建一个活动对象与执行环境关联,活动对象将拥有一个名为arguments的属性,其值为存放函数参数的一个arguments对象。该活动对象将作为执行函数代码的变量对象。

 

此时的活动对象被当作变量对象来执行变量实例化

 

活动对象是一种特殊的实现机制,JS程序只能访问活动对象的成员,不能访问活动对象本身。


6、arguments对象(Arguments object)

 

 

当JS跳转到函数代码执行环境时,将创建一个arguments对象,该对象有如下特点:

(1) arguments对象的prototype原型为Object对象的原型对象,该原型对象的值为即Object.prototype;

(2) arguments有一个callee属性,其初始值为被执行的函数对应的函数对象(即函数自身),该特性使得匿名函数也可以递归调用。

(3) arguments有一个length属性,用来告诉实参的个数;

(4) 对于实参将依次放入arguments[0], arguments[1],  ...  , arguments[n];

 

 

图:实验1中第一次执行函数fun时arguments对象属性


 

 

 

 

 

 

 

 

7、预编译(预处理

JS引擎以段为单位来解释和执行js,一个<script>标签为一段。在执行js之前有一个预编译过程,预编译主要是创建变量对象。

1、在全局代码中,变量对象为window对象;
2、在执行函数代码时,将创建一个活动对象(activation object)作为变量对象;


8、代码的执行

 

8.1 全局代码的执行

  Step 1: 创建作用域链,此时的作用域链中仅有一个对象全局的window对象;

  Step 2: window对象为当前执行环境的变量对象,用来执行变量实例化和变量的值的计算;
  Step 3: 此时的this指针为window对象;

8.2 函数代码的执行

  Step 1: 创建一个活动对象,并把活动对象加入函数定义时所创建的作用域链(该作用域链是在定义函数时创建的,存储在创建该函数对象时所创建的Function对象的Scope属性中);故此时的作用域链为: [活动对象函数定义时的scope]。(强调:也就是说函数代码的执行环境与函数定义时的环境相关,与调用该函数的环境无关)。
  Step 2: 把活动对象作为变量对象,在函数体内进行变量实例化(请参看变量对象一节);
  Step 3: 此时的this指针为调用该函数的对象,如ob.func();此时fun()函数中出现的this为ob;如果调用该函的对象为空,则this为window对象,如:func(),此时函数体内的this指针为window(this指针所指代对象的基本原理)。

说明:
(1) 每个函数定义时,都将关联到一个作用域链。在全局代码中函数, 函数定义时其作用域链只有一个对象window;
(2) 在函数A体内定义的函数B其作用域链为 : [A函数的活动对象,Window对象],如果函数B内还定义有一个函数C,则函数C的作用域链为:[B函数的活动对象,A函数的活动对象,Window对象]

 

8.3 Eval代码的执行

  eval中执行的代码视为在当前代码中添加了一段代码即可。

 

代码分析一

 

<script>
var var1;
var2 = 'var2';
fun1();
function fun1(){
	alert('fun1');
	var3 = 'var3';
}
var var4 = function(){
	alert('匿名函数');
};

var var5 = function fun2(){
	alert('fun2');
};

for(var i=0; i<10; i++){
	var var6 = i*i;
}
var var6;
var5();
fun3();
var4();
</script>
<script>
var var7;
fun3();
function fun3(){
	alert('fun3');
}
</script>
 
程序解读:
(1) 第2行执行之前window对象已经存在,且已预编译完该script标签中的代码。此时的window对象有如下特点:
  • i, fun1, var1, var4, var6都作为属性添加到变量对象window中。其中fun1的值为一个Function对象,其它变量的值都是undefined。
  • 变量i是在for语句结构中定义的, var6是在for语句循环体中定义的,此时都已添加;
  • var7, fun3并没有添加,因为它在另外一个script标签中,var2也没有添加,因为它没有通过关键字 var 来定义
  • var4对应的匿名函数表达式和var5对应的有名函数表达式都没有添加到window中,因为它们是函数表达式,执行的时候才进行计算。var4和var5的值为undefined,而fun1的值不是undefined;
  • 作用域链为: [window]

(2) 执行第3行时,因为程序对一个没有定义的变量var2赋值,系统将隐式的定义一个变量var2,并作为属性添加到window中;第3行执行结束后window.var1 = 'undefined'; window.var2 = 'var2';

 

(3)执行第4行时,将在当前作用域链中,进行一次变量识别,在变量对象window.fun1中找到了fun1的值,并执行fun1。

 

(4) 第9行执行完后,window.var4的值为:通过匿名函数表达式创建的一个Function对象。

 

(5) 执行第13行后window.var5的值为:通过有名函数表达式创建的一个Function对象。但是此时window对象中并不存在fun2这个属性;

 

(6) 执行完第19行后,window.var6赋值为81;

 

(7) 执行完第20行后,window.var6仍然是81,因为这只是一个变量定义语句;

 

(8) 执行第22行时出错,因为在window中找不到名为fun3的属性,故返回null.fun3,试图计算null.fun3()时报错; 

 

(9) 在一个执行块(script)中出错后,该块中的后续JS将不再执行,故var4()不会执行;但是后面script标签中块将继续执行

 

(10) 执行第26行之前,此时会预处理该script标签中的JS源码,流程和处理第一个script标签相似。

 

(11) 执行第27行时将弹出fun3。

 

 


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics