V8 何时开始编译和执行与事件循环堆栈相关的代码?

When does V8 starts compiling and executing the code in relation to the event loop stack?

一直很好奇js代码是怎么从头到尾执行的

我已经阅读了有关事件循环的内容并看到了这个 great video, 堆栈帧看起来如何 here,and also read about how the V8 engine compiles js code here.

问题:

V8什么时候开始编译和执行与事件循环栈相关的代码?

是函数即将出栈的时候吗?

或者所有函数都在被放入堆栈之前就被编译了吗?

因此将其他函数放在顶部的过程实际上只处理机器代码,如果是这样,在从堆栈中弹出函数时会执行该机器代码吗?

如果我的问题没有被理解,我相信通过这个例子会更好理解

示例:

function foo() {
    var name=`foo`;
    var obj = {
        number: 6
    }
    console.log(obj.number);
}

function baz() {
    var name = `baz`;
    console.log(a);
    foo();
}

baz();
  1. 发生的第一个过程是 lazy parsing,其中正在分析所有文件的语法错误,但未完全分析,因此花费的时间较少。
  2. 正在执行函数 declerations

    • v8 引擎现在是否将函数声明代码编译为 机器码?还是还没轮到他..
  3. baz 被调用,baz 被放置在堆栈的底部,并且在它的堆栈帧中存储了名称变量值(因为它是一个原语)。

    • buz 何​​时被解析并转换为机器代码?在将其放入堆栈之前?或者它什么时候弹出?
  4. console.log 放在 baz 之上并被执行,- 控制台显示 baz

    • 这是 console.logjs代码编译成机器码执行的地方?
  5. console.logs 弹出堆栈。

  6. foo 放在 baz 之上,obj 放在堆中(因为它是引用类型),name=foo 放在 foo 的栈帧中。

  7. console.log 在 foo 之上执行,控制台显示 6.

  8. console.log 弹出。
  9. foo 弹出,连同它的局部变量值。
  10. baz 与它的 name=baz 局部变量一起弹出

没有"the event loop stack"。

一个概念是"call stack",这是一个术语,指的是当函数相互调用时,它们形成了一个类似堆栈的当前状态。这主要是一个理论概念,但碰巧确实有一个名为 "the stack" 的内存区域,用于函数的局部变量,但它不是具有 push/pop 的数据结构接口:调用函数的行为将它的数据放在这个堆栈上,从函数返回时再次删除它,将控制权返回给调用函数。

这回答了你的部分功能:开始执行一个函数实际上与将这个函数放在调用堆栈上完全一样。这是对同一事物的两种描述。

另一个概念是事件队列。您可以将其视为等待执行的函数队列;每当没有其他函数在执行时,就会调用此队列中的下一个函数。将一个函数放入队列不需要它已经被解析或编译。在您的示例代码段中,根本没有使用事件队列。

编译函数真的和这一切无关。当一个函数被调用时(由另一个函数或事件循环),它必须以某种形式可执行——但是根据你的 JavaScript 引擎,它可以在没有任何编译的情况下被解释,或者它可以得到编译为字节码,或者它可以编译为机器码,或者引擎可以利用这个机会从一个切换到另一个。

由于您专门询问了 V8:在当前版本中,当 V8 看到像 function f() { ... } 这样的函数定义时,它还没有做任何事情(除了少数情况下 V8 猜测该函数将很快就会被执行,在这种情况下它会立即为它创建字节码)。如果该函数作为回调排队,仍然没有解析或编译发生。当第一次调用函数时,V8 会为其创建字节码。再次调用该函数时,字节码已经存在,不需要额外的工作。当一个函数运行得足够热时,V8 最终决定为其编译优化的机器代码,通常是在后台线程上。对它的额外调用是 V8 检查后台线程是否已经完成生成机器码的机会;如果是这样,那么下一次调用将使用优化后的代码,而不是像之前的调用那样解释函数的字节码。请注意,这些实施细节会随着时间而改变。

再说明一点:

in its stack frame the name variable value is stored (since its a primitive).

不完全是。 变量本身存储在堆栈帧中,但仅作为参考。它是否指原始值并不重要;字符串和对象都分配在堆上。当函数 returns 及其堆栈框架被拆除时,局部变量将被销毁;堆上的相应对象或字符串将(最终,在某个不确定的时间)被垃圾收集器清理。