【灵魂拷问】当面试官问你JavaScript预编译

6,157 阅读4分钟

(一) 前言

在腾讯字节等其他大厂的面试中,JavaScript预编译是经常会被问到的问题,本文将带大家了解JS预编译中的具体过程

(二)编译执行步骤

传统编译语言编译步骤

对传统编译型语言来说,其编译步骤一般为:词法分析->语法分析->代码生成,下面让我们来分别介绍这3个过程。

  1. 词法分析 这个过程会将代码分隔成一个个语法单元,比如var a = 520;这段代码通常会被分解为vara=520这4个词法单元。

  2. 语法分析 这个过程是将词法单元整合成一个多维数组,即抽象语法树(AST),以下面代码为例

if(typeof a == "undefined" ){ 
a = 0; 
} else { 
a = a; 
} 
alert(a);

语法树.jpg

当JavaScript解释器在构造语法树的时候,如果发现无法构造,就会报语法错误(syntaxError),并结束整个代码块的解析。

  1. 代码生成 这个过程是将抽象语法树AST转变为可执行的机器代码,让计算机能读懂执行。

JavaScript编译步骤

比起传统的只有3个步骤的语言的编译器,JavaScript引擎要复杂的多,但总体来看,JavaScript编译过程只有下面三个步骤: 1. 语法分析 2. 预编译 3. 解释执行

(三)预编译详解

预编译概述

JavaScript预编译发生在代码片段执行前的几微秒(甚至更短!),预编译分为两种,一种是函数预编译,另一种是全局预编译,全局预编译发生在页面加载完成时执行,函数预编译发生在函数执行的前一刻。预编译会创建当前环境的执行上下文。

函数的预编译执行四部曲

  1. 创建Activation Object(以下简写为AO对象);
  2. 找形参和变量声明,将变量声明和形参作为AO的属性名,值为underfined;
  3. 将实参和形参值统一;
  4. 在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体。

案例代码

//请问下面的console.log()输出什么?
function fn(a) {
  //console.log(a);
  var a = 123//变量赋值
  //console.log(a);
  function a() { }//函数声明
  //console.log(a);
  var b = function () { }//变量赋值(函数表达式)
  //console.log(b);
  function d() { }//函数声明
}
fn(1)//函数调用

根据上面的四部曲,对代码注解后,我们可以很轻松的知道四个console.log()输出什么,让我们来看下AO的变化

  1. 创建AO对象
AO{
    //空对象
}
  1. 找形参和变量声明,将变量声明和形参作为AO的属性名,值为undefined;
AO{
    a: undefined
    b: undefined
}
  1. 将实参和形参值统一;
AO{
    a: 1,
    b: undefined
}
  1. 在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体。
AO{
    a: function(){}
    b: undefined
    d: function(){}
}

最后,下面是完整的预编译过程

AO:{
a:undefined -> 1 -> function a(){}
b:undefined
d:function d(){}
}

全局的预编译执行三部曲

  1. 创建Global Object(以下简写为GO对象);
  2. 找形参和变量声明,将变量声明和形参作为GO的属性名,值为undefined;
  3. 在全局里找函数声明,将函数名作为GO对象的属性名,值赋予函数体。

案例代码

global = 100;
function fn() {
  //console.log(global);
  global = 200;
  //console.log(global);
  var global = 300;
}
fn();

根据全局预编译三部曲我们可以知道他的GO变化过程

  1. 创建GO对象
GO{
    // 空对象
}
  1. 找形参和变量声明,将变量声明和形参作为GO的属性名,值为underfined
GO: {
  global: undefined
}
  1. 在全局里找函数声明,将函数名作为GO对象的属性名,值赋予函数体
GO: {
  global: undefined
  fn: function() { }
}

注意这里函数声明会带来函数自己的AO,预编译过程继续套用四部曲即可

(四)总结

当遇到面试官问你预编译过程时,可以根据上面的内容轻松解答,同时面试时也会遇到很多问你console.log()输出值的问题,也可以用上面的公式获取正确答案。

本文章已收录JavaScript技术详解,更多JavaScript技术可查看专栏。 ❤码字不易点个免费的赞吧❤