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)作为变量对象;
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。
分享到:
相关推荐
photoshop-cc-javascript-ref-2019学习教程
JavaScript --- ES6 涵盖(ES6 -- ES11)语法学习
photoshop-cc-javascript-ref-2015学习教程
2.写xpath时,尽量从一个具有id的元素开始,这样也可以大大提高执行速度,例如 如果上面的测试写成下面这样,运行时间就会变成几秒了。 assertTrue(selenium.isElementPresent("//div[1]/table/tbody/tr[2]/td[2]...
javascript完美学习视频教程-----11.javascript流程控制-跳转语句、with语句
javascript经典特效---每日一语.rarjavascript经典特效---每日一语.rarjavascript经典特效---每日一语.rarjavascript经典特效---每日一语.rarjavascript经典特效---每日一语.rar
Html-Javascript-Tutorial.pdf Javascript Demystified (2013).pdf JavaScript Examples Bible.pdf JavaScript Pocket Reference 2nd Edition.pdf JavaScript Reference Guide.bak.pdf Javascript 王者归来.pdf ...
JavaScript高效图形编程--高清版.pdf 个人收集电子书,仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!
javascript经典特效---又一搜索引擎.rar javascript经典特效---又一搜索引擎.rar javascript经典特效---又一搜索引擎.rar
javascript经典特效---一个个出现的文字.rar javascript经典特效---一个个出现的文字.rar javascript经典特效---一个个出现的文字.rar
高性能JavaScript编程-高清-带书签.pdf高性能JavaScript编程-高清-带书签.pdf高性能JavaScript编程-高清-带书签.pdf高性能JavaScript编程-高清-带书签.pdf
Javascript学习第一季--Javascript DOM 总结
手册-JavaScript进阶学习 手册-JavaScript进阶学习 手册-JavaScript进阶学习