JS----预编译及变量提升详解

2019-11-18    seo达人

JS----预编译及变量提升详解

JS属于解释型语言,在执行过程中顺序执行,但是会分块先预编译然后才执行。因此在JS中存在一种变量提升的现象。搞懂预编译环节,变量提升自然而然也就懂了。本文讲围绕以下几点进行介绍(变量提升会穿插在其中讲解):



预编译执行步骤

示例演示



预编译执行步骤

预编译发生在函数执行的前一刻,过程如下:



创建AO对象,执行期上下文(后面更新关于执行期上下文详解)。

寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined.

将形参和实参相统一,即更改形参后的undefined为具体的形参值。

寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。



至此,预编译环节结束,函数中咯变量按照最终AO对象中的值开始执行。接下来,结合示例演示就会更加清晰。



作者:北海北方

链接:https://juejin.im/post/5aa6693df265da23884cb571

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。



示例演示

我们先来看下面这段代码:

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);



作者:北海北方

链接:https://juejin.im/post/5aa6693df265da23884cb571

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

接下来我们来按照前面的步骤详细分析它的预编译执行过程:



创建AO对象



AO{

    //空对象    

}

复制代码

找形参和变量声明



AO{

    a : undefined,

    b : undefined

}

复制代码

形参和实参相统一



AO{

    a : 1,

    b : undefined

}

复制代码

找函数声明



AO{

    a : function a(){},

    b : undefined,

    d : function d(){}

}

复制代码预编译环节就此结束,此时的AO对象已经更新为:

AO{

    a : function a(){},

    b : undefined,

    d : function d(){}

}

复制代码函数开始逐行顺序执行:

 function fn(a){

    console.log(a);// 输出functiona(){}

    var a = 123;//执行到这里重新对a赋,AO对象再一次更新

    console.log(a);// 输出123

    

    function a(){};//预编译环节已经进行了变量提升,故执行时不在看这行代码

    console.log(a);// 输出123

    

    var b = function(){};//这个是函数表达式不是函数声明,故不能提升,会对AO中的b重新赋值

    console.log(b);//输出function(){}

    

    function d(){};

 }

复制代码至此,函数执行完毕,销毁AO对象。

我们再来看几个例子,熟悉函数的预编译过程。

示例一:

function test (a,b){

    console.log(a);

    c = 0;

    var c;

    a = 3;

    b = 2;

    console.log(b);

    function b(){};

    function d(){};

    console.log(b);



//调用函数

test(1);

复制代码它的AO创建过程如下(此处省略创建空AO对象的部分,下文同):

AO1{

    a : undefined,

    b : undefined,

    c : undefined

}



AO2{

    a : 1,

    b : undefined,

    c : undefined

}



AO3{

    a : 1,

    b : function b(){},

    c : undefined,

    d : function d(){}

}

复制代码至此预编译环节完成,开始执行:

function test (a,b){

    console.log(a); //输出1

    c = 0; //给AO对象中的c重新赋值0

    var c;//预编译环节变量提升,不再读此行代码

    a = 3;//给AO对象中的a重新赋值3

    b = 2;//给AO对象中的b重新赋值2

    console.log(b);//输出2

    function b(){};//预编译环节变量提升,执行时不再读这行代码

    function d(){};//预编译环节变量提升,执行时不再读这行代码

    console.log(b);//输出2



//调用函数

test(1);



复制代码示例二:

这个例子中我们引入全局对象GO。GO与AO的过程类似

function test(){

var a = b = 123;

}

test();

复制代码此函数的执行过程:先把123赋给b,再声明a,再把b赋给a。此时变量b未经声明就赋值,为全局变量。预编译环节如下:

GO1{

b : undefined

}

AO1{

a : undefined

}



GO2{

    b : 123;

}

AO2{

    a : 123;

}

复制代码示例三 :

console.log(test);

function test(test){

   console.log(test);

   var test = 234;

   console.log(test);

   function test(){};

}

test(1);

var test = 123;

复制代码我们来看它的预编译过程:

//执行前(页面加载完成时)生成GO对象

GO1{

    test : undefined

}

GO2{

    test : function(){}

}



//输出 function test(){...}



//执行test()前生成它的AO对象

AO1{

    test : undefined

}

AO2{

    test : 1

}

AO3{

    test : function test(){}

}



//预编译结束开始执行test(1);

AO4{

    test : 234

}

//输出234

复制代码示例四:

function demo(){

    console.log(b);

    if(a){

        var b = 100;

    }

    console.log(b);

    c = 234;

    console.log(c);

}

var a;

demo();

a = 10;

console.log(c);

复制代码我们来看它的预编译过程:

//首先是全局对象GO 

GO1{

    a : undefined

}

G02{

    a : undefined,

    demo : function demo(){}

}

//执行demo()前预编译,由于demo中的c未声明就使用故为全局对象



//输出undefined

GO3{

    a : undefined,

    demo : function demo(){}

    c : undefined

}

//此时a还是undefined,故不执行if()代码块

//输出还是undefined

GO4{

    a : undefined,

    demo : function demo(){}

    c : 234;

}

//输出234

GO5{

    a : 10,

    demo : function demo(){}

    c : 234;

}

//输出234


分享本文至:

日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档