执行环境和作用域
我们知道,所有 JavaScript 代码都是在一个执行环境中被执行的。执行环境是一个概念,一种机制,用来完成JavaScript运行时在作用域、生存期等方面的处理,它定义了变量或函数是否有权访问其他数据,决定各自行为。
在javascript中,可执行的JavaScript代码分三种类型:
1. Global Code,即全局的、不在任何函数里面的代码,例如:一个js文件、嵌入在HTML页面中的js代码等。
2. Eval Code,即使用eval()函数动态执行的JS代码。
3. Function Code,即用户自定义函数中的函数体JS代码。
不同类型的JavaScript代码具有不同的执行环境,这里我们不考虑evel code,对应于global code和function code存在2种执行环境:全局执行环境和函数执行环境。
在一个页面中,第一次载入JS代码时创建一个全局执行环境,全局执行环境是最外围的执行环境,在Web浏览器中,全局执行环境被认为是window对象。因此,所有的全局变量和函数都是作为window对象的属性和方法创建的。
当我们谈到执行环境的时候,我们需要思考浏览器解析JS代码的过程。一般的话分为两个阶段:
1,JS解析器查找执行上下文进行预解析 2,逐块执行JS代码
1.预解析阶段
我们先看三段代码:
script中直接输入后发现,二三两个代码是undefined值,说明浏览器已经知道了a是undefined,但是不知道a=10。
通过预解析机制,JS解析器会先将 var定义的变量名存储,而不会为其赋值。 a 存储在执行环境中,因为没有赋值,所以 a 的值会被初始化为undefined的值。
变量的赋值是在赋值语句执行的时候执行的。
还有一种情况,那就是关于函数的声明和表达式的,如下:
通过对预解析机制的简单了解我们可以知道只有函数声明的方式才可以将函数执行写在函数声明之前,而使用函数表达式定义的函数则不能先调用后声明。
同时:在JS解析器进行预解析的时候会遵循一个原则,变量声明和函数声明重名的话会优先存储函数声明,与先后顺序无关,
执行顺序是:“fir”→“sec”→“fou”→“fif”→“seven”(在这里,a函数没有在全局函数中运行。)
在第一步预解析的过程中,a最后存储为 function a(){alert(4);} ,而在第二步执行的过程中,a反复被重新赋值,且函数重新声明不影响a的赋值。
在这里,可以看到在函数名相同的情况下,函数名属于后定义的函数。后定义的函数会覆盖先定义的函数。
这个例子是js高级程序中的例子,输出结果:
但是如果如果在函数中,除了以上数据之外,还会有其他数据。先看以下代码:
输出:
以上代码展示了在函数体的语句执行之前,arguments变量和函数的参数都已经被赋值。从这里可以看出,函数每被调用一次,都会产生一个新的执行上下文环境。因为不同的调用可能就会有不同的参数。
因此,如果是代码段是函数的话,函数的执行上下文环境需要在上面“准备工作”基础上,添加:
看下面的例子:
输出:
在上面这个例子中参数的定义会被解析成局部变量定义,所以在函数内部预解析的时候会将局部变量a进行预解析。
综合全文,给执行上下文环境下一个通俗的定义——在执行代码之前,把将要用到的所有的变量都事先拿出来,有的直接赋值了,有的先用undefined占个空。