JavaScript 预编译原理及经典面试题

    技术2025-09-21  74

    JS 语言的特点

    在深入了解 JavaScript 的预编译之前,不得不先来回忆一下 JS 语言的特点:

    解释型语言: 区别于编译型语言,逐行编译,就是编译一行,执行一行,而且对速度要求并不是太高;JS 引擎是单线程的;JS 符合 ECMA 标准;JS 执行队列类似于轮转时间片。

    JS 运行三部曲

    在这里,最重要的就是第一点:解释型语言的运行过程。 JS 运行有三部曲:

    语法分析:很简单,就是通篇扫描一下有没有低级语法(语义)错误;预编译: 简单地说就是在内存中开辟了一些空间,存放一些变量与函数;解释执行:解释一行,执行一行。

    JS 预编译实例

    下面正式进入预编译的介绍,看例子为什么控制台可以打印出 ‘a’

    <script type="text/javascript"> test(); function test() { console.log('a'); } </script>

    如上边示例,因为有预编译的存在,test() 的调用虽然在声明之前,函数也可以执行。这是为什么呢?引出 预编译前奏 这个概念:

    预编译前奏:

    imply global 暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有; eg: a = 123; eg: var a = b = 123; 一切声明的全局变量,全是 window 的属性。 eg: var a = 123;

    下图为一个实例,变量 a 虽然声明了,但不是全局变量,所以不能在函数体外访问到,但是变量 b 满足预编译前奏的第一点,未经声明就被赋值,此变量为全局对象所有,所以可以访问到。 知道了预编译前奏的概念,那么我们的理解就已经深入了一半了,顺着脉络继续。预编译前奏生成 GO(Global Object)对象,之后如果有函数就进行预编译,预编译四部曲如下:

    预编译四部曲:

    创建 AO 对象(Activation Object 俗称执行期上下文);找形参和变量声明,将变量和形参名作为 AO 属性名,值为 undefined;将实参值和形参统一;在函数体里面找函数声明,值赋予函数体。

    通过简单的实例来认识一下 GO 与 AO 的结合:预编译前奏时,生成 window.b = 10;①预编译时创建 AO 对象;②将变量声明 a 放入 AO,值为 undefined;③传入参数,使得实参形参相统一,本例中没有形参;④如果函数体里还有函数声明,值赋予函数体,但是本例中没有。 最终,函数会打印出 undefined 。 再来一个例子,跟着我们前边的节奏来分析,先写下 GO 对象,再找 AO 对象。

    <script type="text/javascript"> global = 100; function fn() { console.log(global); global = 200; console.log(global); var global = 300; } fn(); var global; </script>

    大家先来分析一下分别会输出什么呢?

    我们来分析一下,global 未经声明就赋值,生成 GO 对象 global = 100;在执行到 fn() 的前一刻进行预编译,生成 AO 对象,其中只有 global,值为undefined,之后既没有实参的传入,也没有函数声明,那么第15行就打印 undefined了,经过16行之后,undefined 被修改为200 。所以两个答案分别是 undefined 和 200,你答对了吗?

    百度2013面试题:

    题目一: 因为在函数 bar 最顶端return foo,那我们直接看到 AO 的第四部:返回函数声明,那么程序会打印 function foo()。

    题目二: 这题和上一个类似,因为函数的最后返回 foo,而 foo 在上边被赋值过,那么 foo 一定为该值咯。 看完之后我们发现,其实预编译也并不难,再梳理一下:

    总结

    预编译前奏:生成 GO 对象,有两个规则; 预编译四部曲: 1.创建 AO 对象 2. 找形参和变量声明,将变量和形参名作为 AO 属性名,值为 undefined; 3. 将实参值和形参统一; 4. 在函数体里面找函数声明,值赋予函数体。

    底层基础决定上层建筑,这是不变的真理,深究原理绝对是没错的!

    说在最后的话:编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

    Processed: 0.009, SQL: 9