深入理解js代码执行过程----详解函数提升和变量提升

深入理解js代码执行过程----详解函数提升和变量提升

console.log(myname);//undefined
var myname = 'why';
foo(123);
function foo(num) {
  console.log(m);//undefined
  var m = 10;
  var n = 20;
  console.log("foo")//foo
}

以上代码输出结果如下:
2.png

由此引发思考,为什么myname在未执行到var myname = 'why';就可以使用了呢?

为什么foo函数可以在定义前调用呢?

在JavaScript中,前者的现象称为变量提升,而后者称为函数提升

接下来,让笔者详细解析这段代码的执行过程是如何进行的。

首先js引擎会对这段代码进行编译。

编译阶段(编译阶段会忽略函数执行语句,只编译声明和定义语句),会首先创建一个执行上下文栈(Exection context Stack)(也可以称为调用栈),简称ECStack。

3.png

在ECStack中会先创建一个全局执行上下文GEC(global exection context),在GEC中存储着一个变量对象简称VO对象(variable Object),它指向堆内存中的全局对象简称GO对象(Global Object)

这个GO存储着js各种各样的原生类(ES5中称为构造函数)以及其他的API,比如Array,Function,Object,setTimeout,sessionStorage(仅限浏览器)等等,在浏览器执行环境中,该对象就是我们熟知的window,而在node执行环境中,它被称为global。

除了原生的各种API,就是我们新创建的变量myname,此时myname被赋值为undefined,而foo是函数,当编译到这段函数定义时,并不会给foo简单的赋值为undefined,而又在堆内存开辟一块空间用于存储foo函数对象,foo函数对象里面存着 父级作用域以及foo函数体

4.png

来到执行阶段,首先执行console.log(myname),此时GO中的myname为undefined,

这就是变量提升,虽然执行console.log(myname)的时候还没有给myname赋任何值,但是在编译阶段就将myname赋值为undefined

当执行到 var myname = ‘why’时,GO中的myname会被赋值为’why’,

当执行foo(123)时,则会去GO查找foo,得知是一个函数,又会在栈里开辟一个foo的函数执行上下文,存着一个VO指向AO(Activation Object活跃对象),此时未真正执行,先编译foo函数,所以在堆内存中开辟了一块空间存储foo的AO对象,里面的num、m、n都为undefined

5.png

当编译完foo函数时来到执行阶段,

//foo函数体
function foo(num) {
  console.log(m);
  var m = 10;
  var n = 20;
  console.log("foo")
}

console.log(m),m未赋值前为undefined,接着m赋值为10,n赋值为20,最后打印"foo"

这就是函数提升,原理与变量提升相同,在代码编译阶段就已经存在foo函数了,所以执行foo(123)可以在GO中找到相对应的函数体,从而创建新的函数执行上下文。

6.png

当执行完foo(123)后,foo的函数执行上下文会被弹出,foo的AO对象没有被引用之后,就会被js引擎回收,至此,这段代码的执行流程就走完了

如果执行完foo(123),又跟着个foo(321),同样会再次创建一个新的foo函数执行上下文,并创建新的foo的AO对象

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值