预编译-面试官:这题的输出结果是什么?

129 阅读4分钟

话不多说,先直接上题:请说出下面代码的执行结果

function fn(a){
    var a=1
    var a=2
    function b(){}
    var b=a
    a=function c(){}
    console.log(a);
    c=b
    console.log(c);

}
fn(3)

//结果:[Function:c]   2

对于能清楚地确定结果并解释原理的小伙伴,应该对预编译这个考点已经熟悉。在了解预编译之前我们需要知道一个小知识点——声明提升

声明提升

声明提升,就是在编译时,将当前作用域的变量的声明提升到这个作用域的顶端,也将函数的声明整体提升

foo()
function foo(){
    console.log(a)
    var a = 1
}

例如上面的代码,函数foo的调用在函数的声明之前,但是在编译时,foo的函数声明也就是整个函数体被提升到前面,再调用。在函数内部,console.log(a) 在变量a的声明之前,在编译时,会把a的声明提升到函数作用域的顶端,但是并不会执行赋值操作,所以打印的a的值为undefined,打印之后才执行赋值

预编译

预编译就是在代码执行之前,对代码进行编译,再执行代码操作,有函数预编译全局预编译

函数预编译

函数的预编译主要有四部曲,可以根据这四部曲去分析代码执行的结果

1.创建一个AO对象
2.找形参和变量声明,将变量声明和形参作为AO的属性名,值为undefined
3.将实参和形参统一
4.在函数体内找函数声明,将函数名作为A哦对象的属性名,赋予函数体

借此,我们用四部曲来分析开头的题目:

function fn(a){
    var a=1
    var a=2
    function b(){}
    var b=a
    a=function c(){}
    console.log(a);
    c=b
    console.log(c);

}
fn(3)

首先,函数的调用带来函数的编译,开始编译:(以下代码区只为了方便理解,不考虑运行)

第一步:创建一个AO对象: AO = {}

第二步:找形参和变量声明,将变量声明和形参作为AO的属性名,值为undefined: 形参是a,变量声明有var a; var b; 值都为undefined,相同属性名的会被覆盖只保留一个,即:

AO = {
    a:undefined,
    b:undefined
}

第三步:将实参和形参统一: 实参是3,形参是a,所以a的值为3,即:

AO = {
    a:3,
    b:undefined
}

第四步:在函数体内找函数声明,将函数名作为AO对象的属性名,赋予函数体: 函数体内内的函数声明有 function b(){} 和 function c(){} ,即:

AO = {
    a:3,
    b:function b(){},
    c:function c(){}
}

到这里预编译就算完成,之后执行代码操作:赋值a为1,赋值a为2,再把a赋值给b,即:

AO = {
    a:2,
    b:2,
    c:function c(){}
}

然后将 function c(){} 赋值给a,再打印a,即a的值就是 function c(){}

AO = {
    a:function c(){},
    b:2,
    c:function c(){}
}

最后将b赋值给c,再打印c,即 c = 2

AO = {
    a:function c(){},
    b:2,
    c:2
}

所以最终运行结果为:[Function: c] 2

全局预编译

全局预编译和函数预编译类似,但是只有三个步骤,因为全局没有参数

1.创建一个GO对象
2.找变量声明,将变量声明作为GO对象的属性名,值为undefined
3.在全局找函数声明,将函数名作为GO对象的属性名,值赋予函数体

可以举个例子分析一下:

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

第一步:创建一个GO对象: GO = {} 第二步:找变量声明,将变量声明作为GO对象的属性名,值为undefined: 这里只有一个变量声明 var global; 即:(以下代码区只为分析理解)

GO = {
    global:undefined
}

第三步:在全局找函数声明,将函数名作为GO对象的属性名,值赋予函数体: 这里只有一个函数声明 function fn(){} ,即:

GO = {
    global:undefined,
    fn:function fn(){}
}

至此,全局预编译完成,开始执行全局代码:将global赋值为 100,即:

GO = {
    global:100,
    fn:function fn(){}
}

然后调用函数fn,所以开始执行函数预编译:

第一步创建AO对象,第二步找形参和变量声明,没有形参和函数体内的函数声明,即:

AO = {
    global:undefined
}

函数预编译结束,开始执行函数内代码操作:先打印global,值为undefined,然后赋值global为200,再打印global,值为200,最后再赋值global为300,所以执行结果为:undefined 200

预编译在面试过程中也是比较容易遇到的,充分熟悉预编译的步骤了,就这?

最后,我们练习一下,可以把结果写在评论:

    function fn(a, b) {
        console.log(a);
        c=0
        var c
        a=3
        b=2
        console.log(b);
        function b(){}
        function d(){}
        console.log(b);
    }
    fn(1)
    console.log(d);
    d = 5
    var d;