执行环境, 执行栈, 作用域
JavaScript中执行环境, 执行栈, 作用域的介绍及实例.
执行环境和词法环境
执行环境(Execution Context):
执行环境包括全局执行环境和函数执行环境.
- 当JavaScript代码最初开始运行时,全局执行环境被创建.
- 当某个函数被调用时,函数执行环境被创建.
词法环境(Lexical Environment):
- 描述变量名与特定变量或函数的关系
- 基于词法嵌套结构
词法环境一般来说和作用域(scope)同义,详见.
全局环境和全局对象
当JavaScript代码开始刚开始运行时,全局执行环境就会被创建.在JavaScript中全局指代码或变量不包含在某个function对象中.
在全局执行环境中,JavaScript引擎会创建全局对象
和一个特殊的变量this
,全局执行环境下两者的值相同.
在一般的执行环境中,还会有outer environment,全局环境是处于最外层的outer environment.
在全局环境中定义变量和函数会自动将变量和函数与全局对象关联. 以浏览器为例:a === window.a
func() === window.func()
函数调用和执行栈
函数每被调用一次,都会创建一个执行环境置于执行栈顶部,调用自己本身也会创建.
1 | function b(){ |
这段代码执行过程:
- 编译器编译源代码;
- 全局执行环境被创建, b和a保存于内存空间;
- 代码一行行执行;
- 执行到
a();
时,新的执行环境被创建; - 新的执行环境在执行栈中全局环境上方,执行栈顶部的执行环境处于运行状态;
- 函数a中调用
b()
,此时在执行栈顶部的执行环境又变成b; - 当b执行结束时,跳出执行栈,然后是a,然后是全局.
作用域
变量名相同,在不同的作用域中,就是不同的变量,在内存空间中占用的是不同的位置.
1 | function b(){ |
变量声明提前 Hoisting
1 | b(); |
在大多数编程语言中,会出现错误,因为代码一般是一行一行执行的,但是由于JavaScript中存在变量声明提前,所以出现上述结果.
而a的结果是undefined的原因是var a = 'Hello';
中提前的只有var = a;
,赋值的部分并未被提前.
相当于:
1 | var a; |
上述代码只是为了便于理解,实际上并不是真正以这样的方式执行.
实际原因是执行环境的创建存在两个阶段:
1. 创建阶段
在这个阶段,JS引擎为变量和函数设置内存空间,在代码开始一行一行执行时,就可以获取内存空间中存在的变量或函数.其中函数整体保存于内存空间,而变量只有声明存在,所赋的值不会保存于内存中,因此在这个阶段,所有的变量值都为undefined.在JavaScript中,所有变量的初始值都为undefined.
2. 执行阶段
在这个阶段,代码开始一行一行执行.
作用域链
1 | function b(){ |
每个执行环境都可以获取其外部环境中存在的变量,b的外部环境是全局环境,因此可以获取到全局环境中声明的myVar
.这里的外部环境是词法意义上(lexical environment)的.
在不同的作用域调用同一个函数,变量的值也可能会有差异.
1 |
|
此时函数b的外部词法环境是a,因此会从a中获取myVar
的值.
作用域与ES6中的let
在执行环境的创建阶段,变量依然会被初始化为undefined被保存在内存空间,但只有在执行阶段执行到那一行代码时,变量才可以被使用.
1 | console.log(c); |
1 | console.log(c); |
用了let,js引擎就有了块级作用域.
1 | if (true){ |
1 | if (true){ |
参考