Google V8 预解析嵌套函数时的运行机制是什么?

问题描述 投票:0回答:1

我了解了V8的预解析和全解析。由于lazying pasring,如果解析器遇到函数声明,它会跳过函数内部的代码,并且不会为其生成AST和字节码,而只生成顶层代码的AST和字节码。相反,当解析顶层代码遇到函数时,预解析器会对函数进行快速预解析。目的是:判断当前函数是否存在语法错误,如果发现,则将被抛出。检查函数内部是否引用外部变量。如果是,则将外部变量复制到堆中,下次执行函数时,直接使用堆中的引用。

以下面的代码为例,显然't'在它的[[Scopes]]头部有一个closure(a) {i: 1}(你可以在Chrome上运行这段代码),我的问题是'a '是一个三层嵌套函数,当'a'在全局环境中第一次预解析时,预解析器找到函数'a'中内部函数引用的变量的算法是什么,递归或通过其他方式?如果是递归的话,先找到函数b,然后扫描b里面的函数也就是函数c,c会不会再解析2次才真正被调用?

function a() {
    let i = 1;
    function b(){
        function c() {
            console.log(i);
        }
        return c;
    }
    return b;
}
let t = a();
console.dir(t);


javascript algorithm compilation v8
1个回答
0
投票

我认为你的问题在https://v8.dev/blog/preparser#variable-allocation中得到了解答。

为了避免非线性性能开销,我们甚至在准备期间也执行全范围解析。我们存储了足够的元数据,以便以后可以简单地跳过内部函数,而不必重新准备它们。一种方法是存储内部函数引用的变量名。这存储起来很昂贵,并且需要我们仍然重复工作:我们已经在准备期间执行了变量解析。

相反,我们将变量分配的位置序列化为每个变量的密集标志数组。当我们延迟解析函数时,变量会按照预解析器看到的顺序重新创建,并且我们可以简单地将元数据应用于变量。

所以根据我的理解,在

a
的第一个“准备”中,它将找到变量
i
,还将扫描
b
c
,并找到
i
的用法,将其标记为堆分配。之后
a
可以在不进一步解析
b
c
的情况下执行,并且如果它们曾经被执行过,
i
将已经在堆上分配并可以被内部函数使用。

© www.soinside.com 2019 - 2024. All rights reserved.