js中的预编译

常见的编译语言,如Java,编译步骤分为:词法分析–>语法分析–>语义检查–>代码优化和字节码生成

​ 而对于解释型语言,如JavaScript,通过词法分析–>语法分析–>语法树,就可以开始解释执行了。

​ js的具体执行过程为:词法分析【将字符流转换为记号流】、语法分析【分析为AST语法树】、预编译、解释执行。

1、词法分析:将字符流转换为记号流

2、语法分析:在函数执行前一刻,将程序大致粗略的扫描一遍,检查是否存在语法错误,然后生成对应的语法树,如:括号是否正确对应等。

3、预编译:当JavaScript引擎解析脚本时,他会在预编译阶段对所有声明的变量和函数进行处理,并且是先预声明变量,再预定义函数!

4、解释执行:在执行过程中,JavaScript引擎是严格按照作用域(scope)机制来执行的,并且JavaScript的变量和函数作用域是在定义是决定的,而不是执行时决定的。JavaScript中的变量作用域在函数体内有效,无块作用域;

function test(){
   for(var i = 0;i<3;i++){
       执行语句
   }
   console.log(i);  //i仍然有值,且为3  ;但这个在java语言中,则无效
}

​ JavaScript引擎通过作用域链scope chain把多个嵌套的作用域串联在一起,并借助这个链条帮助JavaScript解释器检索变量的值。这个作用域链相当于一个索引库,并通过编号来存储它们的嵌套关系。当JavaScript解释器检索变量的值,会按着这个索引编号进行快速查找,直到找到全局对象global object为止,如果没有找到值,则传递一个特殊的undefined 值。

scopeTest('global');
function scopeTest(scope){
  console.log(scope);
  var scope = "local";
  function scope(){
  
  }
  console.log(scope);
}   //打印结果为:function scope(){}
    //            local

可能大多数小伙伴都觉得是global local ,其实不然,这个是预编译导致的情况,那现在我们来说说预编译。

预编译前奏:

​ 1、imply global 暗示全局变量;即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。

​ eg:a = 123;

​ eg:var a = b = 123; //b未经声明就赋值

​ 2、一切声明的全局变量,全是window的属性。

​ eg:var a = 123; ----->window.a = 123

预编译:

  1. 创建AO对象 Activation Object 【我们所说的作用域,也称为执行器上下文】
  2. 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
  3. 将实参值和形参统一
  4. 在函数体里面找到函数声明,值赋予函数体

通过预编译的这4个步骤,我们便可以来理解上面那个例题:

​ 预编译发生在函数执行的前一刻,首先按步骤来,第一步,创建AO对象,AO{ };

​ 第二步,按第2条来,形参名为scope,且函数中的变量声明中也有一个scope,虽然两者同名,但是在AO对象中只会存储一个scope,得出 AO{ scope:undefined} ;

​ 第三步,按第3条来,将实参值和形参统一,也就是,将实参中传递过来的值赋值给形参中用到的变量,得出 AO{scope:“global”} ;

​ 第四步,找函数体中的函数声明,值赋予函数体 ,我们很容易看到,函数scopeTest中,包含了一个函数scope的声明且函数名scope与AO对象的scope同名,而AO对象对待同名也只能存储一个,接着按照第4条,将函数体赋给AO对象中的scope,得出 AO{scope:function scope(){ }} ;

​ 预编译结束 ,然后一步一步执行代码。第一行调用scopeTest(‘global’)函数,最开始输出scope的值,电脑会从AO对象里面拿东西,里面scope存储的值为function scope(){}, 然后输出函数。接着继续执行代码,scope重新赋值,scope= “local”; 然后 function scope(){}之前已经在预编译中获取了,当前可不必理会,最后再次需要输出scope中的值,现在AO对象里面为:AO{scope:“local"},所以输出local 。所以最后的结果为:function scope(){} local

通过上面这道题,我们差不多可以理解预编译的过程了,那再完成下面一道题,来巩固一下。

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);
//输出结果为:
 //  function a(){}
 //  123
 //  123
 //  function(){}

预编译发生在函数执行的前一刻,我们再次按上面的步骤来,第一步,创建AO对象,AO{ };

​ 第二步,按第2条来,形参名为a,且函数中的变量声明中也有一个a,虽然两者同名,但是在AO对象中只会存储一个a,接着还有一个变量声明b,得出 AO{ a:undefined,b:undefined} ;

​ 第三步,按第3条来,将实参值和形参统一,也就是,将实参中传递过来的值赋值给形参中用到的变量,得出 AO{a:1,b:undefined} ;

​ 第四步,找函数体中的函数声明,值赋予函数体 ,我们很容易看到,函数fn中,只包含了函数a声明与函数d声明两个,【注意:这里的函数b不是函数声明式定义,而是表达式定义】接着按照第4条,将函数体赋给AO对象中的对应的属性,得出 AO{a:function(){},b:undefined,d:function(){}} ;

​ 预编译结束 ,然后一步一步执行代码。调用fu(1)函数,最开始输出a的值,电脑会从AO对象里面拿东西,里面a存储的值为function a(){}, 然后输出a,也就是function a(){ }。接着继续执行代码,到var a = 123; 因为预编译时,这里的a已经被声明了,所以只会执行后面的赋值,a重新赋值,a = 123 然后输出a的值;然后到 function a(){ },因为预编译已经经过了声明阶段,现可忽略该行,然后输出a的值,123;接着到 var b = function(){ },同上面的a类似,在预编译时,b已经被声明了,所以只执行后面的函数,现在AO对象里面为:AO{a:123,b:function(){},d:function d(){}},然后输出b,为function (){} 。所以最后得出上面显示的结果。

从上面这个我们便可以理解预编译的过程,在函数内部执行出现的局部作用域会产生AO对象,而在外部的全局作用域会产生GO对象,两者覆盖的范围不一样,全局的GO对象的预编译跟AO对象的步骤、原理差不多,甚至更容易理解。接着举个例子来理解一下:

function test(){
    console.log(b);
    if(a){
        var b = 100;
    }
    c = 234;
    console.log(c)
}
var a;
test();
a = 10;
console.log(c)

首先也是预编译,但现在是先建立GO对象【全局对象,window 对象】,GO{ };然后变量提升,函数提升,GO对象为 GO{a:undefiend,test: function(){}} ;接着一步一步执行代码,声明test函数与声明变量a已经在执行代码前的预编译建立GO对象后被执行过了,现在可忽略,到调用test()函数这行代码,建立局部作用域AO对象,然后按照之前的四个步骤,可以得出AO对象为{b:undefined},然后执行test函数中的代码,首先先输出b的值,undefined;然后进行if判断,局部作用域AO中没有a,所以沿着上面查找,找到GO对象中,在GO对象中有a,且值为undefined,所以if判断结果为假,不执行if中的语句;接着c=234,沿着AO对象 查找,没找到c变量,然后接着往上到GO对象 ,【如果变量未经声明就赋值,此变量就为全局对象所有】,所以将c变量加到 GO对象中,并同时赋值为234,所以现在GO对象为 GO{a:undefiend, test:function(){ }, c : 234 },然后输出c的值,输出的是GO对象中的c的值为234。test函数执行结束,然后接着执行下一句,a = 10,此时的a也是全局对象GO的a,所以GO对象又重新变为 GO{a:10, test:function(){ }, c : 234 },最后一条语句,输出c的值,值为 234,所以最后的输出结果为:undefined 234 234

以上文字均为自己在学习时的总结 ,如有错误,或表述不对的地方,请多包涵,ps:可以在评论区交流哟!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值