JavaScript性能优化 - 堆栈中的 JS 执行过程

110 阅读2分钟

接下来我们看一下,关于 JS 代码在执行过程中偏底层所发生的事情。这样的操作可以更加具象的表达出一段代码在栈内存和堆内存里是如何执行的,同样也有利于我们理解 GC 回收内存的工作过程。

let a = 10;
function foo (b) {
    let a = 2;
    function baz (c) {
        console.log(a + b + c)
    }
    return baz
}
let fn = foo(2)
fn(3) // 7

栈内存 ECStack 执行环境栈:用于存放执行上下文

  • EC baz

  • this = window

  • <baz.ao, foo.ao, vo>

  • AO:

  • arguments(0:3)

  • c = 3

  • console.log(a + b + c)

  • 3 + 2 + 2

  • EC foo

  • this = window This 指向

  • <foo.ao , vo> 作用域链

  • AO:

  • arguments:(0: 2)

  • b = 2

  • a = 2

  • baz = AB2 [[scop]] foo.AO

  • EC G 全局执行上下文:

  • VO:

  • a = 10 存放在栈内存

  • foo = AB1 [[scop]] VO 引用类型

  • fn = AB2

  • fn (3)

这个流程走完了以后,就意味着我们当前整个过程也就执行完毕,剩下的事情栈区里面的东西就叫给了 JS 主线程进行回收。

堆内存:

  • AB1 :  

  • function foo (b) { ... }

  • name: foo

  • length: 1 // 形参个数

  • AB2:

  • function bax (c) { ... }

  • name: baz

  • length: 1 // 形参个数

堆内存里的内容所占据的空间会交给 GC 垃圾回收机制进行回收。根据刚才的分析其实主线程里的内容,每次在执行上下文里面的代码执行完成之后,都会检查下要不要把堆内存里的内容进行回收。比如 baz 中的代码在执行完成以后,其中的所有变量在当前作用域里的代码在执行完成以后其他作用域里面根本就没有对它进行引用。所以说这个时候的空间肯定要被进行回收的,也就是所谓的出栈,这个时候完成这个流程之后得出的结果是 7 。

总结:JS 代码在开始执行之后首先会在堆内存里面创建一个执行环境栈,并用来存放不同的执行上下文;第二、代码从上往下执行最先创建的 EC G 也就是全局的执行上下文,在它里面把全局作用域下面的一些代码都进行一些声明和存放;再有就是基本类型的数据是直接存放在栈内存里,对于引用类型的数据存放在堆区中并有 GC 来回收处理,而栈里的内容也就是出栈都是由 JS 主线程来处理。每当遇到函数执行的时候都会重新生成一个执行上下文进栈,代码执行完成以后由是否产生了闭包来决定当前的的上下文里面所引用的堆到底要不要去被释放掉。