![04b9ef9c22dde9e10b559be5c22a40b6.png](https://img-blog.csdnimg.cn/img_convert/04b9ef9c22dde9e10b559be5c22a40b6.png)
背景
首先让我们观察下面的代码,并猜测输出的结果
func()
console.log(string)
var string = '字符串'
function func() {
console.log('Hello world')
}
按顺序执行的话
- 第一行调用func函数时,func函数并没有被声明,应该报错。
- 第二行在控制台打印string变量时,string变量也未被声明,也应该报错。
然而实际情况是
- 第一行成功打印了hello world。
- 第二行打印输出了undefined。
如果把第三行代码注释掉,此时第二行代码会报错。
至此我们可以得出三个结论:
- 在执行过程中如果变量未被声明,则会报错。
- 如果在一个变量被声明之前使用它,则它的值为undefined。
- 如果在函数被声明前调用它,函数可以被正确的运行。
变量提升
通过对上面代码的测试,我们发现在代码真正运行之前JavaScript引擎会预先处理好变量和函数的声明,这个过程就是变量提升(Hoisting)。
所以原本的一行语句其实分为声明和赋值两个步骤,而在代码分析的过程中,用于声明的代码会先被处理。则文章开头的代码可以被分成两个过程:
// 声明
var string = undefined
function func() {
console.log('Hello world')
}
// 运行
func()
console.log(string)
string = '字符串'
JavaScript的执行过程
这里我们先把JavaScript从代码到运行分为两个阶段:编译和运行。
![553b36d158920bacae4b10870d425708.png](https://img-blog.csdnimg.cn/img_convert/553b36d158920bacae4b10870d425708.png)
一段代码在经过编译后,代码会被转换成执行上下文和可执行代码。
执行上下文
执行上下文(Execution context)是JavaScript执行一段代码时的执行环境。其中包含了this的指向,变量环境和词法环境。
执行上下文分为三种类型:
- 全局执行上下文:这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
- 函数执行上下文:每当函数被调用时,就会创建一个函数上下文。不同的函数执行上下文不同,同一个函数被多次调用时,会产生多个执行上下文。
- Eval函数执行上下文:不常使用,这里不做介绍。
我们可以把执行上下文想象这如下这种结构:
ExecutionContext = {
ThisBinding = <this value>, // this的指向
LexicalEnvironment = { ... }, // 词法环境
VariableEnvironment = { ... }, // 变量环境
}
所以在func函数被调用前,执行上下文被创建后,其结构如下:
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: { ... },
VariableEnvironment: { // 变量环境
EnvironmentRecord: {
Type: "object",
// 在这里绑定标识符
string: undefined, // 默认值
func: < func >
}
outer: <null>
}
}
FunctionExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: { ... },
VariableEnvironment: {
EnvironmentRecord: { ... },
outer: <GlobalLexicalEnvironment>
}
}
代码在执行后,执行上下文会变成:
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: { ... },
VariableEnvironment: { // 变量环境
EnvironmentRecord: {
Type: "object",
// 在这里绑定标识符
string: "字符串", // 赋值语句生效
func: < func >
}
outer: <null>
}
}
总结
至此,我们由JavaScript中的变量提升引出了JavaScript的执行上下文概念。
- 在代码中使用var, function定义的变量将会被执行上下文记录在变量环境中。
- 由var,function定义的的变量会在执行上下文创建的时候被声明及赋予默认值(var与function不同)。
- 可执行代码在运行时会从执行上下文中读取变量,如果在修改前访问,则读取其默认值。
- 如果变量提升过程中存在同名变量,则后一个会覆盖前一个。