深入解析JavaScript预编译机制与执行流程

作者:异步漫步时间:2026-10-22 20:39:05浏览:575

本文详细解析JavaScript引擎在代码执行前的预编译过程,区分脚本级与函数级的预编译差异。通过GO(全局对象)与AO(活动对象)的创建步骤,阐明变量声明提升与函数声明提升的具体表现,并结合实例代码展示执行流程中的值变化。

JavaScript运行三部曲

通常认为JavaScript属于解释型语言,执行时逐行解析并运行,但这是否意味着不存在预编译阶段?在脚本实际运行前,JavaScript引擎究竟执行了哪些准备工作?接下来我们将深入探讨这一过程。

1-JavaScript运行三部曲

语法分析

预编译

解释执行

语法分析主要检查代码是否存在基础语法错误;解释执行则是真正运行代码的过程;而预编译可以简单理解为在内存中分配空间,用于存储变量和函数定义。

JS预编译时机

2-JS预编译什么时候发生

预编译具体在何时触发?许多人误以为它仅发生在script标签内代码执行之前。虽然这没错,预编译确实会在script代码执行前发生,但它更频繁地出现在函数执行之前。

实例分析与执行流程

3-实例分析

首先需要明确两个核心概念的区别:变量声明(使用var关键字)与函数声明(function语句)。


<script>
var a = 1;
console.log(a);
function test(a) {
 console.log(a);
 var a = 123;
 console.log(a);
 function a() {}
 console.log(a);
 var b = function() {}
 console.log(b);
 function d() {}
}
var c = function (){
console.log("I at C function");
}
console.log(c);
test(2);
</script>

分析过程如下:

页面加载时,系统会创建GO全局对象(Global Object),即window对象;

随后加载第一个脚本文件;

脚本加载完成后,引擎会检查语法是否合法;

接着开始预编译:查找变量声明,将其作为GO的属性,初始值设为undefined;查找函数声明,同样作为GO的属性,值赋予完整的函数体;

预编译


//抽象描述
  GO/window = {
    a: undefined,
    c: undefined,
    test: function(a) {
      console.log(a);
      var a = 123;
      console.log(a);
      function a() {}
      console.log(a);
      var b = function() {}
      console.log(b);
      function d() {}
    }
  }

最后进入解释执行阶段,直到遇到调用函数test(2)的语句;


//抽象描述
  GO/window = {
    a: 1,
    c: function (){
      console.log("I at C function");
    }
    test: function(a) {
      console.log(a);
      var a = 123;
      console.log(a);
      function a() {}
      console.log(a);
      var b = function() {}
      console.log(b);
      function d() {}
    }
  }

在调用函数test()之前,会先发生预编译过程: 创建AO活动对象(Active Object);

查找形参和变量声明,将其值设为undefined;

将实参的值赋给对应的形参;

查找函数声明,将函数体赋值给函数名属性;

预编译之前面1、2两小步如下:


//抽象描述
  AO = {
    a:undefined,
    b:undefined,
  }

预编译的第三步如下:


//抽象描述
    AO = {
      a:2,
      b:undefined,
    }

预编译的第四步如下:


//抽象描述
  AO = {
    a:function a() {},
    b:undefined
    d:function d() {}
  }

执行test()函数时的过程变化如下:


//抽象描述
  AO = {
    a:function a() {},
    b:undefined
    d:function d() {}
  }
  --->
  AO = {
    a:123,
    b:undefined
    d:function d() {}
  }
  --->
  AO = {
    a:123,
    b:function() {}
    d:function d() {}
  }

最终执行结果:

JavaScript运行三部曲

注意:

在预编译阶段,系统只处理变量声明和函数声明,不涉及初始化(赋值)操作,且匿名函数不参与预编译;只有在解释执行阶段,才会进行变量的实际初始化。

预编译规则总结

预编译(函数执行前)

创建AO对象(Active Object)

查找函数形参及函数内部的变量声明,将形参名及变量名作为AO对象的属性,初始值设为undefined

实参与形参统一,将实参的值赋给形参

查找函数声明,将函数名作为AO对象的属性,值设为函数引用

预编译(脚本代码块script执行前) 查找全局变量声明(包括隐式全局变量,即省略var的声明),将变量名作为全局对象的属性,初始值设为undefined

查找函数声明,将函数名作为全局对象的属性,值设为函数引用

预编译小结

预编译两个小规则

函数声明整体提升-(具体点说,无论函数调用和声明的位置是前是后,系统总会把函数声明移到调用前面)

变量 声明提升-(具体点说,无论变量调用和声明的位置是前是后,系统总会把声明移到调用前,注意仅仅只是声明,所以值是undefined)

预编译前奏

imply global 即任何变量,如果未经声明就赋值,则此变量就位全局变量所有。(全局域就是Window)

一切声明的全局变量,全是window的属性; var a = 12;等同于Window.a = 12;

函数预编译发生在函数执行前一刻。

作者最新文章