这是我参与更文挑战的第30天,活动详情查看: 更文挑战
为什么需要了解执行上下文?
理解了JavaScript执行上下文,才能更好的理解JavaScript语言本身,比如变量提升,作用域和闭包等。
先来看段代码测试一下吧
show()
console.log(Name)
var Name = 'AN'
function show(){
console.log('我出现了')
}
大家都知道JavaScript是解释型的语言,就是说应当是一行行顺序执行的,那是不是应该是
- show()的时候,因为show()还没定义,直接报错
- console.log(Name),Name也没定义,报错
实际的结果是什么呢
可以看到虽然show()之前并未定义show()函数,可却正确的执行出来了。这个地方我们可以得出一个结论:就是说执行前函数直接被移到调用函数的前面去了。这叫变量提升。
console.log(Name)结果并不是AN,而是undefined。这是怎么回事呢?难道说是只要console.log(变量)就会自动给分配个undefined吗?试着不声明直接随便console.log一个变量试试
console.log(Anyone)
由此可以看出,声明变量是不能缺的。我们又知道var Name = 'AN'是可以拆分成var Name(声明)和Name = 'AN'(赋值)的,
而在JavaScript中仅声明未赋值的变量默认的值就是undefined,自然可以想到,当console.log(Name)在前的时候起作用的仅仅只有var Name这部分,可以简化为以下代码验证一下
console.log(Name)
var Name
可以看到和上面是一样的结果。也意味着,JS在执行的时候仅仅只是提前了声明而并没有赋值。这样我们就可以得出一条结论:JavaScript在执行过程中会将变量的声明var Name提前,这个概念也叫做变量提升。
变量提升
还是刚才的案例
show()
console.log(Name)
var Name = 'AN'
function show(){
console.log('我出现了')
}
模拟一下变量提升的情况
//变量提升部分
var Name
function show(){
console.log('我出现了')
}
//执行部分
show()
console.log(Name)
实际上这就是为什么可以在定义之前使用变量或者函数的原因,因为函数和变量在执行之前都提升到了代码的开头。这就叫变量提升。那这个执行之前到底是啥意思?啥就叫执行之前?
那就得提提JavaScript代码的执行流程了。
一段JavaScript代码>>编译阶段>>执行阶段 一段代码在被执行之前需要被JavaScript引擎编译,编译之后才进入执行阶段。可以看出这里的执行之前就是编译阶段。
编译阶段都做了啥事,经过编译阶段后,会生成两个部分:执行上下文和可执行代码,执行上下文是JavaScript执行一段代码时的运行环境,包括变量环境和词法环境。
show()
console.log(Name)
var Name = 'AN'
function show(){
console.log('我出现了')
}
上面的Name和show函数都保存在变量环境(Viriable Eviroment)这个对象中。变量环境的样子可以简单理解为
VariableEnvironment:
Name -> undefined,
show ->function : {console.log('我出现了')}
比如调用一个函数,就进入这个函数的执行上下文,确定函数再执行期间的this,变量,对象,函数等。
编译过程中对这段代码都干了什么?
- 第1行和第2行不涉及声明,所以JS引擎不处理
- 第3行是用
var声明了变量Name,因此JavaScript引擎会在变量环境中创建一个名为Name的属性,并且用undefined对其进行初始化 - 第4行,JavaScript引擎发现了一个function定义的函数,会将函数定义存储到堆(HEAP)中去,并且在变量环境中创建一个show属性,这个属性指向堆中函数的位置。
这样就生成了变量环境对象,接着JavaScript引擎会把声明外的代码编译成字节码对应就是可执行代码
show()
console.log(Name)
Name = 'AN'
执行阶段
得到了可执行代码之后,就按照顺序一行行执行
-
执行show函数的时候,JavaScript引擎在变量环境对象中查找这个函数,因为变量环境对象里存着这个函数的引用,因此JavaScript执行直接就出结果了。”我出现了“
-
打印Name信息,JavaScript引擎在变量环境对象中查找改对象,并且此时值为undefined,输出undefined
-
执行第三行,把
"AN"赋给Name变量,赋值后的Name变量属性值变为了"AN",变量环境对象如下VariableEnvironment: Name -> "AN", showName ->function : {console.log("我出现了")}
如果代码中出现重名的变量或者函数怎么办
function show(){
console.log("我出现了")
}
show()
function show(){
console.log("出现了但是没完全出现,哎,就是玩儿")
}
show()
在编译阶段,执行上下文阶段,先存入了第一个show,存入第二个的时候会把第一个覆盖掉。执行阶段,然后两个show()出来的结果就是
以上就是JavaScript执行顺序的简单总结了。