JS中的预编译(AO、GO详解)


执行js文件的流程:
①通篇检查语法错误
②预编译
③解释一行执行一行

一、由实例引发的思考

首先我们来看两个例子:

        test()
        function test() {
            console.log(1) //1
        }

可以看到先执行函数,后声明函数是可以的。

        console.log(a) //undefined
        var a=20
        
console.log(a) //Uncaught ReferenceError

可以看到,先打印a,后定义a会undefined;而直接打印a会报错
那么也就意味着,我们的js文件执行前,会将变量的声明提升,赋值不提升。
总结:会将函数声明+定义提升、变量的声明提升,赋值不提升。
但这种说法还是太过简略,一些复杂的问题就无法解决。
此外你要明白一个基本概念:暗示全局变量
暗示全局变量:只要未声明直接赋值,暗示为全局变量,存到window对象内。

            function f()
            {
            var a=5;
            l=4
            }
            f()
            console.log(l)
            console.log(a)

在这里我们打印a会报错,因为a为局部变量;而l因为没有使用var 所以被暗示为全局变量,打印值为4

二、全局上下文GO:

1.GO===Window,(GO对象的创建发生在在页面加载完成时的那一刻)也就是全局执行时的前一刻。
2.步骤如下:
①寻找var变量声明(在GO对象中即为键为变量名,值为undefined)
②寻找函数声明并赋值函数。(在GO对象中即为键为函数名,值为函数体)
③执行。
举例一

            console.log(a,b)
            function a(){}
            var b=function(){}

GO对象创建过程:
①寻找var变量声明,并赋值为undefined

GO=
{
	b:undefined
}

②寻找函数声明,赋值函数

GO=
{
	b:undefined
	a:function a(){}
}

③执行:

    console.log(a,b)//GO对象中a:function a(){} b:undefined
    function a(){}
    var b=function(){}

怎么样?有没有豁然醒悟的感觉,如果没有,别急,我们继续向下看:
实例二

            var x=1,y=z=3
            function add(n)
            {
                return n=n+1
            }
            y=add(x)
            function add(n)
            {
                return n=n+2
            }
            z=add(x)

GO对象创建过程:
①寻找var变量声明,并赋值为undefined

GO=
{
	x:undefined
	y:undefined
	z:undefined
}

②寻找函数声明,赋值函数

GO=
{
	x:undefined
	y:undefined
	z:undefined
	add:function add(){return n=n+1}
	->function add(){return n=n+2}
	
}

③执行:

  			var x=1,y=z=3//x:undefined->1
						 //y:undefined->3
						 //z:undefined->3
            function add(n) //已提升
            {
                return n=n+1
            }
            y=add(x)//y:undefined->3->add
            function add(n)
            {
                return n=n+2
            }
            z=add(x)//z:undefined->3->add
            console.log(x,y,z)//1 3 3

三、函数上下文AO:

1.当一个函数被调用时,首先创建函数上下文这个对象(所以AO对象创建在函数执行的前一刻)
2.步骤如下:
①寻找函数形参、变量声明(设定为undefined)。(在AO对象中即为键为形参或变量名,值为undefined)
②将实参赋值给形参。(在AO对象中即为键为形参,值为实参)
③寻找函数声明并赋值函数。(在AO对象中即为键为函数名,值为函数体)
④执行函数。
举例一

        function test(a)
        {
            console.log(a)
            
            var a=1
            console.log(a)
            
            function a(){}
            console.log(a)
            
            var b=function(){}
            console.log(b)
            
            function d(){}
            console.log(d)
        }
        test(2)

AO对象的构建过程:
①寻找函数形参、变量声明。

AO=
{
	//①寻找函数形参、变量声明。
	a:undefined 
	b:undefined
}

②赋值形参

AO=
{
    //②形参赋值。
	a:undefined ->2
	b:undefined 
}

③寻找函数声明并赋值函数。

AO=
{
   //寻找函数声明并赋值函数。
	a:undefined ->2->function(){}
	b:undefined ->function(){}
	d:function(){}
}

④执行:

function test(a)
        {
            console.log(a) //现在的AO中 a:function(){}
            
            var a=1 //a:undefined ->2->function(){}->1
            console.log(a) //1
            
            function a(){} //已提升不再执行
            console.log(a)//1
            
            var b=function(){}//已提升步不再执行
            console.log(b)//现在的AO中 b:function(){} function(){}
            
            function d(){}
            console.log(d)//现在的AO中 d:function(){}
        }
        test(2)

举例二、
相信经过上个例子,你已经懂得了这个过程,那我们在来看一个例子:

            function test(a,b)
            {
                console.log(a)
                c=0
                var c
                a=5
                b=6
                console.log(b)
                function b(){}
                function d(){}
                console.log(b)
            }
            test(1,2)

AO对象创建过程:
①寻找函数形参、变量声明

AO={
	a:undefined
	b:undefined
	c:undefined
}

②赋值形参

AO={
	a:undefined->1
	b:undefined->2
	c:undefined
}

③寻找函数声明,赋值函数

AO={
	a:undefined->1
	b:undefined->2->function b(){}
	c:undefined
	d:function d(){}
}

④执行:

            function test(a,b)
            {
                console.log(a) //AO对象中 a:1
                c=0  c:undefined->0
                var c
                a=5 //undefined->1->5
                console.log(b)//AO对象中:function b(){}
                b=6//undefined->2->function b(){}->6
                console.log(b) //AO对象中:b:6
                function b(){} //已提升不再更新AO
                function d(){}//已提升不再更新AO
                console.log(b) //AO对象中:b:6
                console.log(d)//AO对象中:function d(){}
            }
            test(1,2)

总结:预编译做过的事执行就不要再做!!

四、全局上下文GO+函数上下文AO:

实例一

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

GO对象的构建:

GO:
{
	//1.变量声明:undefined
	a:undefined
	//函数声明:函数体
	test: function test(){}
	c:function c(){}
}

执行:

GO
{
	//执行
 	var a = 1;	//a:undefined->1
    console.log(a);//1
    function test(a) {...}//已提升、不执行
    function c() {...}//已提升、不执行
    console.log(c,test);//GO中:function c(){...},function test(){...}
	 test(2);//执行testAO
}

AO对象的创建:

testAO:
{
	//形参、变量定义为undefined
	a:undefined
	b:undefined
	//形参赋值
	a:undefined->2
	//寻找函数赋值函数
	a:undefined->2->function(){}
	b:undefined
	d:function(){}
}

test执行:

 function test(a) {
console.log(a);//AO对象中a:function(){}
var a = 123;//a:undefined->2->function(){}->123
console.log(a);//AO对象中a:123
function a() { }//已提升
console.log(a);//AO对象中a:123
var b = function () b:undefined->function(){}
console.log(b);//AO对象中b:function(){}
d=2//d:function(){}->2
function d() { }//已提升
console.log(d)
        }

在这里插入图片描述
实例二

        var b=3
        console.log(a)
        function a(a)
        {
            console.log(a)
            var a=2
            console.log(a)
            function a(){}
            var b=5
            console.log(b)
        }
        a(1)

GO对象的构建:

GO:
{
	//1.变量声明:undefined
	b:undefined
	//函数声明:函数体
	a: function a(){...}
}

执行:

GO
{
	//执行
 	var b = 3;	//b:undefined->3
    console.log(a);//a: function a(){...}
    function a(a) {...}//已提升、不执行
	 a(1);//执行aAO
}

aAO:

AO:
{
	//形参、变量定义为undefined
	a:undefined
	b:undefined
	//形参赋值
	a:undefined->1
	//寻找函数赋值函数
	a:undefined->1->function(){}
}

a执行:

function a(a){
console.log(a) //AO:a:function(){}
var a=2//a:undefined->1->function(){}->2
console.log(a)//AO:2
function a(){}//已提升
var b=5//b:undefined->5
console.log(b)AO:5
        }

结果为:
在这里插入图片描述
实例三(坑点)

a=1
console.log(a)
function test()
{
    console.log(a)
    a=2
    console.log(a)
    var a=3
    console.log(a)

}
var a
console.log(a)
test()
console.log(a)

相信现在的你已经足够熟练了,那在这里我就不再写的那么详细了,直接写执行,一起看下坑点:

GO:
{
	a:undefined->1
	test:function(){}
}
AO:
{
	a:undefined->2->3
}

那么问题来了 我们在执行test的时候第一个a打印出来到底是undefined还是1呢?我们执行完test打印的a为多少?不执行又是多少呢?我们先来看下结果
在这里插入图片描述
我们可以看到在test外的打印a值无论test是否执行,它都为1;而在test内的第一个a为undefined。这说明了一个问题:
AO与GO是独立的 它们各自管理各自的数。但这么说并不准确,因为若某值在AO中没有,GO中有,则在函数中用GO中的值;但反过来AO有GO无,在全局用则会报错。
如果你还是不明白那继续看下面这个例子:
实例三

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

看到这你可能会发出疑问?现在不是讲GO与AO结合么,这里也没有涉及GO,别急,再仔细回忆下前言中我提到的暗示全局变量。
在这个函数里其实我们的b为全局变量而不是局部变量,因为我们只var a;没有var b;所以默认暗示b挂在了window上(注意是test()之后才有了这个暗示)所以GO、AO对象如下:

GO
{
	b:1
	test:test(){}
}
AO:
{
	a:undefined
}

执行:

function test(){          
  var a=b=1  //AO:a:undefined->1 、GO: b:undefined->1
  console.log(a,b) //AO:a:1, GO:b:1
}
test()
console.log(b)//全局有
console.log(a)//全局没有报错

所以我们的执行test时过程为先找AO、AO有a为1,无b,找GO GO有b为1.
而在函数外打印时,报错说明了GO无就是无,无法找AO这一特点。

总结一下:函数执行找AO,AO无找GO
全局执行找GO、GO无就是无
特例:若有if语句怎么办呢?

            function test()
            {
                console.log(b)
                if(a)
                {var b=2}
                console.log(b)
                c=3
                console.log(c)
            }
            var a 
            test()
            a=1
            console.log(a)
            console.log(c)

此时我们的GO、AO对象:

GO:
{
	a:undefined->(test()执行完赋值为1)
	test:function(){}
	c:3
	//TEST执行:
	AO{
		//(c加到全局,因为是暗示全局变量)
		b:undefined(现在的a为undefined,所以b无法赋值)
		
	}
	
}

可能看到这里你有些懵,但是没关系我们结合结果来再说一遍:
在这里插入图片描述

            function test()
            {
                console.log(b) //在AO第一步提升了变量声明
                if(a)//a为GO中的,此时为赋值为undefined,所以无法进行给b赋值
                {var b=2}
                console.log(b) //在AO第一步提升了变量声明
                c=3 //暗示成全局变量
                console.log(c)
            }
            var a //GO第一步已提升
            test()
            a=1
            console.log(a)//赋值为1
            console.log(c)//打印全局变量c

相信你现在已经明白了,那我们再来总结一下:
若if{}内有变量的声明即var…,那么在创建AO的时候将它声明,是否赋值,就要看条件是否满足了。那我们再提升一下:预编译只要有var、let、const等变量声明语句我们就在对象中添加这个值:undefined,至于具体的赋值,根据执行过程来判定。
自己试一下:
1.

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

console.log(test())
console.log(a)

在这里插入图片描述
报错原因是因为这里的a一直都是属于AO 即使a=2在函数里,因为后面有var a也使得它成为了局部变量。
2.

        function test()
{
    return a
    a=1
    function a(){}
    var a =2
}
console.log(test())

在这里插入图片描述
这个结果是因为return后函数不再执行下去。
3.

        a=1
        function test(a)
        {
            function e(){}
            console.log(arguments,a)
            arguments[0]=2
            console.log(arguments,a)
            console.log(e)
            if(a){var b=3}
            var c
            a=3
            var a 
            console.log(b)
            f=5
            console.log(c)
            console.log(a)
            console.log(arguments)
        }
        var a 
        test(4)

在这里插入图片描述
argument[0]即为a改变argument[0]也改变,改变argument[0],a也改变。

总结

总结一下预编译:当一个JS文件执行前首先会进行GO对象的生成->遇到函数执行,则会先进行AO对象生成(可能会有暗示全局变量提升到GO)->函数执行

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值