JS代码执行过程及内存变化

466 阅读3分钟

内存管理

不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存,且内存的管理都会有如下的生命周期:

  • 分配申请你需要的内存(申请);
  • 使用分配的内存(存放一些东西,比如对象等);
  • 不需要使用时,对其进行释放;

js内存管理

  • JS对于基本数据类型内存的分配会在执行时,直接在栈空间进行分配;
  • JS对于复杂数据类型内存的分配会在堆内存中开辟一块空间,并且将这块空间的指针返回值变量引用; image.png

垃圾回收

现代的编程语言都是有自己的垃圾回收机制 常见的GC算法:

  1. 引用计数:
    • 当一个对象有一个引用指向它时,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销毁掉;
    • 这个算法有一个很大的弊端就是会产生循环引用;
  2. 标记清除:
    • 这个算法是设置一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于哪些没有引用到的对象,就认为是不可用的对象。
    • JS引擎比较广泛的采用的就是标记清除算法,但是类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合一些其他的算法。

js代码执行相应的内存变化

JS代码执行过程

初始化全局对象

js引擎在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO),里面会包含Date、Array、String、Number、setTimeout、setInterval等等;其中还有一个window属性指向自己。

执行上下文栈(调用栈)

  • js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈。

调用栈首先会执行全局代码块

  • 全局的代码块为了执行会构建一个Global Execution Context(GEC)
  • 然后GEC会被放入到ECS中执行;

GEC执行前后

  • 执行前:在代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中,但是并不会赋值;这个过程也称之为变量的作用域提升(hoisting)
  • 执行过程:对变量赋值,或者执行其他的函数;

image.png

函数执行

在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且压入到EC Stack中。

FEC执行前后

执行前:

  • 在解析函数成为AST树结构时,会创建一个Activation Object(AO):AO中包含形参、arguments、函数定义和指向函数对象、定义的变量;
  • 第二部分:作用域链:由VO(在函数中就是AO对象)和父级VO组成,查找时会一层层查找;
  • 第三部分:this绑定的值。

执行过程: 对变量赋值,或者执行其他的函数;

执行后: 如果函数不构成闭包,则AO对象会被释放掉,调用栈中FEC也会清空。

image.png -- 文章参考coderwhy老师的课程总结