函数执行与变量提升
我们来看这样一段JavaScript代码,思考打印的结果。
var name = 'zengge'
var num1 = 1
var num2 = 2
var res = num1 + num2
console.log(name)
console.log(res)
很简单对吧,大家都知道函数是由上往下执行的,所以console.log的结果就是zengge和3。那么再看下面这一段代码:
console.log(name)
var name = 'zengge'
foo()
function foo(){
console.log('foo')
}
可能看到这里有些同学会觉得,哎在定义变量和函数之前打印变量和函数,那不是报错么?嗯有可能,但是凡事不能想当然,我们把它放到控制台打印一下看看结果:
哈哈有些同学看到这是不是有些困惑,不是应该报错吗?怎么会打印undefined和foo呢?实际上这涉及到了Javascript的变量提升和函数提升。在此之前我们先来了解一下JavaScript的代码执行过程中到底发生了什么,同样拿上一幅图来举例:
首先所有的JavaScript的代码的执行都会在一个叫做执行上下文栈(Execution Context Stack,简称ECS)的地方执行,ECS是存在于js引擎内部的
ok开始分析:
- 在JavaScript代码执行之前,会在堆内存中创建一个全局对象:
Global Object(GO),该对象中里面会包含Date、Array、String、Number、setTimeout、setInterval等等内置类以及函数,还有一个特殊的window对象指向的是它本身,也就是说你可以window.window.window这样无限套娃(虽然这样没啥意义)。 - 然后就开始执行代码,全局的代码块为了执行会构建一个全局执行上下文
Global Execution Context(GEC),并会创建一个变量对象Vo(Variable Object),此时的Vo就等于Go
注意此时Go中有foo函数,foo函数指向的是一块内存地址(0xa00),里面是具体的执行代码。执行里面的代码之前,js会再创建一个函数执行上下文FEC(Function Execution Context)压入ECS中,并且也会创建一个Vo,此时的Vo指的是活跃对象Ao(Activation Object),函数中的定义的变量,函数等都会存在这个Ao中,并且作用域链和this指向也会在这个里面(之后的章节会讲到)。
随后函数执行完后,打印出foo,FEC和GEC从ECS中弹出,代码执行完毕。\