大家都知道JavaScript是一种解释性语言。既然是解释性语言,就是编译一行,执行一行,那么怎么预编译呢?js引擎为脚本执行做了什么?今天让我们来看看。
1-JavaScript运行三部曲
语法分析
预编译
解释执行
语法分析很简单,就是引擎检查你的代码有没有低级语法错误;说明执行,顾名思义,就是代码执行;对预编译的简单理解就是在内存中开辟一些空间来存储一些变量和函数;
2-JS预编译什么时候发生
预编译到底什么时候发生?认为预编译只发生在脚本中的代码块执行之前并没有错,但是预编译确实发生在脚本代码执行之前,但是大部分发生在函数执行之前
3-案例分析
首先,区分和理解这两个概念:变量声明var.函数声明函数(){}
script var a=1;console . log(a);功能测试(a){ console . log(a);var a=123console . log(a);函数a(){ } console . log(a);var b=function(){ } console . log(b);function d(){ } } var C=function(){ console . log(' I at C function ');} console . log(c);测试(2);/脚本分析过程如下:
页面生成创建一个GO全局对象(即一个窗口对象);
加载第一个脚本文件;
脚本加载后,分析语法是否合法;
开始预编译查找变量声明,作为GO属性,并给出undefined价值;查找函数声明,作为GO属性,给函数体赋值;
预编译
//抽象描述go/window={a:undefined,c:undefined,test 3360 function(a){ console . log(a);var a=123console . log(a);函数a(){ } console . log(a);var b=function(){ } console . log(b);函数d() {}}解释执行代码(直到调用函数测试(2)的语句被执行)
//抽象描述go/window={a: 1,c : function(){ console . log(' I at c function ');} test:函数(a){ console . log(a);var a=123console . log(a);函数a(){ } console . log(a);var b=function(){ } console . log(b);在函数d() {}}执行函数test()之前,会进行预编译以创建AO活动对象。
找到形式参数和变量声明,并给出undefined价值观;
实际参数值被赋给形式参数;
找到函数声明,给函数体赋值;
预编译的前两个小步骤如下:
//抽象描述ao={a:undefined,b:undefined,}预编译的第三步如下:
//抽象描述AO={a:2,b:undefined,}预编译的第四步如下:
//抽象描述ao={a:function a () {},b:function d () {}}执行test()函数时,以下过程会发生变化:
//抽象描述ao={a:function a () {},b : function d(){ }-ao={ a :123,b : undefined d : function d(){ }-ao={ a :123,b : function(){ } { } d : function d(){ } }执行结果:
注意:
变量声明和函数声明发生在预编译阶段,不存在初始化行为(赋值),匿名函数不参与预编译;变量初始化只会在解释执行阶段进行;
预编译(在函数执行之前)
创建AO对象(活动对象)
在函数中找到函数参数和变量声明。参数名和变量名作为AO对象的属性,值未定义
实际参数和形式参数是统一的,实际参数赋给形式参数
找到函数声明,函数名作为AO对象的属性,值作为函数引用
预编译(在执行脚本代码块脚本之前)以查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名作为全局对象和未定义值的属性
查找函数声明,函数名作为全局对象的属性,值作为函数引用
编译前摘要
预编两条小规则
函数声明作为一个整体进行提升-(具体来说,系统总是将函数声明移动到调用的前面,而不管函数调用和声明的位置是在前面还是后面)
变量声明提升-(具体来说,无论在哪里调用和声明变量,系统总是会将声明移到调用的前面。注意,它只是一个声明,因此该值是未定义的)
预编译前奏
暗示全局是任何变量。如果赋值时没有声明,这个变量将归比特全局变量所有。(全局域是窗口)
所有声明的全局变量都是窗口的所有属性;var a=12相当于Window.a=12
函数预编译就在函数执行之前发生。