关于JavaScript的执行环境与做用域的解读javascript
JavaScript高级编程中关于执行环境与做用域的问题在第四章有过说起,可是交代的不是很明确,所以查阅了网上各类资料,对于执行环境以及做用域有了一个初步的认识。java
1、什么是执行环境(execution context) 编程
执行环境在书中是这样定义的:执行环境定义了变量或函数有权访问的其余数据,决定了他们各自的行为。 浏览器
对于execution context的翻译有两种,一种是执行环境,一种是执行上下文。此处使用执行环境的概念。 ide
JavaScript中有如下三种运行环境: 函数
1.全局级的运行环境:默认的代码运行环境,当代码被载入的时候,JavaScript引擎默认进入该环境; this
2.函数级的运行环境:执行流进入函数的时候,运行函数中的代码; spa
3.Eval函数的运行环境:执行Eval()函数内的代码线程
2、什么是做用域 翻译
JavaScript中的做用域分为全局做用域和函数做用域,目前不支持块状做用域。JavaScript的做用是静态做用域也叫词法做用域,意思就是做用域是静态的肯定的。有一些说法常常将做用域与执行环境等同起来,我的的理解是这是两个不一样层次的概念,虽然二者在某些方面的描述着实有些类似,可是并不能等同起来说。做用域不光是针对JavaScript这门语言来讲的,在其余语言中也有这种概念。而执行环境是JavaScript解释器在解释执行的过程当中产生的这种概念。所以我的认为不能等同来说。
3、什么是执行环境栈
浏览器的JavaScript引擎是单线程,所以在某一个时刻只能有一个事件被激活处理,所以便产生了这样一个执行环境栈。JavaScript的代码被载入到后,JavaScript引擎默认进入全局执行环境,当在全局执行环境中调用一个函数的时候,程序的执行流会进入该函数,同时建立该函数的执行环境,而后将该函数的执行环境压入执行环境栈的顶部。浏览器引擎在执行的时候老是在执行环境栈顶部的执行环境中执行。当顶部执行环境中的代码被执行完毕后,该执行环境被从环境栈顶部弹出,而后在其下的执行环境中执行。这样执行环境经历一个不断压栈,弹栈的过程,直到最底部的全局执行环境。
4、执行环境的建立过程
由上面的描述咱们知道,执行环境是一个动态的概念,每当调用一个函数的时候,就会该函数建立一个执行环境。在JavaScript引擎内部这个过程分为两步:
建立阶段
创建变量、函数、arguments对象、参数
创建做用域链
肯定this值
执行阶段
变量赋值、函数引用、执行其余代码
能够把执行环境想象成一个对象,里面包含了变量对象(函数中的arguments对象,参数,内部的变量以及函数声明),做用域链(当前执行环境的变量对象加上父执行环境的变量对象)以及this的值。
5、执行环境对象建立过程详解
根据上面的描述,执行环境对象的建立发生在函数被调用后,真正的函数体被执行之前。在这个过程当中JavaScript引擎会检查函数的参数,变量的声明以及内部的函数,根据得到这些信息建立执行环境对象。这个过程当中变量对象(variable object)、做用域链(scope chain)以及this所指定的对象都会被肯定。建立过程的具体步骤以下:
找到当前执行环境中被调用函数的代码
被调用函数的函数体执行之前,建立被调用函数的执行环境
进入创建阶段:
a.建立执行环境对象:
创建arguments对象、检查当前执行环境中的参数、创建该对象的属性及属性值
检查当前执行环境中的函数声明:
每找到一个函数声明,就在该执行环境对象下用该函数名创建一个属性,属性值就是一个执行该函数在内存中的一个地址的引用。(这就是一个变量提高的过程)若是上述属性值已经在该执行环境对象中存在,就用新的引用值覆盖以前的值(这就是为何在JavaScript中不存在函数重载的缘由)
检查当前执行环境中的变量声明:
每找到一个变量声明,就在该执行环境对象下用该变量名创建一个属性,属性值就是undefined。若是该属性值已经存在的话,直接跳过(这就是为何同名的变量没法覆盖同名的函数声明的缘由),原属性值不会被修改
b.初始化做用域链
c.肯定执行环境中this的指向
代码执行阶段
执行函数体中的代码,给执行环境对象中的属性赋值。
6、实例解析
<script type="text/javascript">
(function(){
console.log('execution context test');
console.log(typeof foo);//function
console.log(typeof a);//undefined
console.log(typeof b);//undefined
console.log(typeof c);//undefined
function foo(){
console.log('foo function');
}
var foo='test';
var a='a';
var b='b';
var c=function(){
console.log('c function');
};
})();
</script>
如上面程序实例所示,建立了一个自执行函数,代码被加载后且代码未被执行前自动建立一个执行环境的对象,而且分析执行环境的参数,而后查找对应的函数声明以及变量声明。这就是为何在打印foo的类型的时候是function的缘由,解析变量声明会被自动赋值为undefind,c为一个函数表达式,所以也被解析为变量,赋值为undefind。在有foo函数声明的前提下,代码中又声明了一个foo的变量,这种状况下在解析变量声明的过程当中,会发如今执行环境对象中有了一个foo的属性,所以直接跳过。