AO是什么?GO是什么?深度解析JS预编译遇见AO和GO

写在前面

三月结束了,四月开始了。希望这个世界早点好起来。

今天是个好日子,好呀嘛好日子。哈哈哈哈哈哈,省里发文件了,终于让我看到了开学的希望,心情格外的舒适。

好了好了,来说说今天都干嘛了吧。今天又了解了一些JS预解析的内容,跟第一次看到的有一些出入,今天来补充一下。也没有很大的区别,主要就是更深入的理解JS预编译的过程。开始总结学习了啊啊啊啊啊啊。

 

JS代码执行过程

我们都知道,JS语言是一种单线程解释性语言。它是一门语言,它有它自己的执行机制。来看下吧。

JS代码执行过程分三个过程

1. 首先,进行语法分析,语法分析就是浏览器先通栏浏览一下整体的代码有没有少写分号、单词拼错等语法的错误。

2. JS预编译。本质是创建AO对象或GO对象,对其属性的操作。

3. 最后解释性执行。就是一行一行的读取代码执行代码。

JS代码执行的过程就是这三个步骤,需要着重理解的就是JS中的预编译的问题。

 

JS预编译

预编译简单理解就是,在代码执行前一刻发生的事情,我们要做到的就是弄清楚具体发生了哪些事情,是怎么发生的,也就是理解JS预编译的过程。全局域和局部域都有预编译的发生,逐个讲解了。以下内容是综合我看到的知识和个人理解的。

函数的JS预编译过程

这里需要先解释一个概念——AO对象。

AO对象:Activation Object,指活动性对象,也叫执行期上下文,就是我们通常所说的作用域。这里指函数的局部作用域。

来个简单的例子体验一下。

       function sum(a, b) {
           console.log(a); 
            var a = 10; 
            console.log(a); 
            var b = 5;

            function arr() {}; // 函数声明
            console.log(b);
            console.log(arr); 
        }
        // 调用函数
        sum(2, 2);

案例函数的JS预编译的过程。 

 调用时,先在函数内部进行js预解析

        1. 先创建AO对象 AO{}

        2. 把形参和变量声明作为对象的属性

        AO {

            a: undefined // 形参a和变量声明的a一样,只需写一个

            b: undefined

        }

        3. 把实参和形参值统一

        AO {

            a: 2

            b: 2

        }

        4. 在函数体中找函数声明,值赋为函数体

        AO {

            a: 2

            b: 2

            arr: function () {};

        } 

 然后进行代码执行,从上往下执行。 

       function sum(a, b) {
           console.log(a); // 去AO对象里找属性a对应的值 2
            a = 10; // 10,到这一步时,AO对象中的a的值被10覆盖 (a:10)
            console.log(a); 
            b = 5; // 到这一步,b覆盖掉原来的值2,变为5
            console.log(b); // 5
            console.log(arr); // function
        }
        // 调用函数
        sum(2, 2);

执行结果 

 

需要注意的点,就是当变量名和函数名一样时,AO对象就添加一个属性,代码往后执行时,属性值会被覆盖掉的。上面的例子就已经很好的解释说明了JS预编译的过程。最最最核心的就是,预编译时AO对象的属性对应的属性值,会在后续代码执行过程中发生变化,也就是会被覆盖

这种过程跟之前我看的过程差不多,当时只是理解到把函数声明和变量提到当前作用域的最前面,没有考虑当变量名和函数名同名时,会遇到的问题。

 

全局预解析

同样的,还是先解释一个概念——GO对象。

GO:Gobel Object,是全局对象,GO对象跟window对象是同一个对象。可以理解为window对象有两个名字 window == GO。

简单的例子

        var b = 33;
        console.log(b); // 33
        // 没有调用函数,不执行
        function arr() { // 这个函数是函数声明
            var a = 11;
            var sum = 5;
        }

        b = function aaa() {}; // 这个是函数表达式 不是函数声明
        // 执行到这一步时,变量b,在GO对象中,上面重新赋值了,所有b的值被新值覆盖
        console.log(b); // function aaa(){}
        console.log(a); // 报错。因为a 是函数内部的变量,属于局部变量,外部函数不能访问 

预编译过程

同样进行预解析

        1.创建GO对象 GO {}

        2.把声明的变量给到GO的属性 赋值为undefined

        GO {

            b: undefined

        }

        3. 找全局域中的函数声明,放到GO对象的属性,赋值为函数体

        GO {

            b: undefined

            arr: function aaa() {}

        } 预解析完毕,执行代码

执行结果

全局域中的预编译,就是对变量和函数声明的提升,过程与函数的预编译很像,就是去掉了形参和实参那一步。

 

预编译案例

来个综合的,稍微复杂点的例子,练练手。

        // 全局域
        a = 100;

        function demo(e) {
            function e() {};
            arguments[0] = 2;
            console.log(e);
            if (a) {
                var b = 0;
            }
            var c;
            c = function sum() {} // 函数表达式,不是函数声明
            a = 10;
            var a;
            console.log(b);
            f = 123;
            console.log(c);
            console.log(a);
        }
        var a;
        demo(1);
        console.log(a);
        console.log(f);

先看结果

 

过程分析

首先是全局域中的预编译,这里只声明了一个变量a,和一个函数demo。所以GO对象

GO {
        变量
        a: undefined
        函数声明
        demo: function () {}
}

 

然后执行代码,到demo,调用demo函数。紧接着对demo函数进行预编译

1 和2创建对象并把变量和形参提出来

 AO {

                形参

                e: undefined

                变量

                b: undefined

                c: undefined

                a: undefined

}

3. 形参实参统一

AO {

                形参

                e: 1

                变量

                b: undefined

                c: undefined

                a: undefined

}

4. 找函数声明, 没有

AO {

                形参

                e: 1

                变量

                b: undefined

                c: undefined

                a: undefined

 }

            执行代码

这就是预编译的过程。最终作用域中的变量和函数都将以AO对象的属性或者GO对象的属性存储着,代码执行时,其中的属性值会随之变化的。好好理解理解,多分析分析过程就好了。

预编译完成后,接着进行代码的执行。

 

整个案例预编译完整过程

最后,附上,案例整个代码的预编译过程,以及其中属性值的变化。 

整给代码预编译的过程,以及对象中属性和属性值的变化 

下面的代码和注释,很重要!!!很重要!!!很重要!!!

        function demo(e) {
            // 1 和2 AO {
            //     形参
            //     e: undefined
            //     变量
            //     b: undefined
            //     c: undefined
            //     a: undefined
            // }
            // 3. 形参实参统一
            // AO {
            //     形参
            //     e: 1
            //     变量
            //     b: undefined
            //     c: undefined
            //     a: undefined
            // }
            // 4. 找函数声明, 没有
            // AO {
            //     形参
            //     e: 1
            //     变量
            //     b: undefined
            //     c: undefined
            //     a: undefined
            // }
            // 执行代码

            function e() {};
            arguments[0] = 2;
            console.log(e); // 2 实参列表的第一个数 与 e映射,arguments[0] == e == 2
            if (a) { // AO中a的值为undefined,转化为数值是NaN,所以下面代码不执行
                var b = 0;
            }
            var c; // 忽略 因为预编译进行过了
            c = function sum() {}
            // AO {
            //     形参
            //     e: 1
            //     变量
            //     b: undefined
            //     c: function sum() {}
            //     a: undefined
            // }
            a = 10;
            // AO {
            //     形参
            //     e: 1
            //     变量
            //     b: undefined
            //     c: function sum() {}
            //     a: 10
            // }
            var a; // 忽略
            console.log(b); // undefined
            f = 123; // 未声明的变量 全局变量,放到GO对象中
            // GO {
            //     变量
            //     a: 100
            //     未声明的变量, 是全局变量
            //     f: 123
            //     函数声明
            //     demo: function () {}
            // }
            console.log(c); // function sum() {}
            console.log(a); // 10
        }
        var a; // 忽略了 ,GO进行过
        demo(1);
        // GO {
        //     变量
        //     a: 100
        //     未声明的变量, 是全局变量
        //     f: 123
        //     函数声明
        //     demo: function () {}
        // }
        // AO {
        //     形参
        //     e: 1
        //     变量
        //     b: undefined
        //     c: function sum() {}
        //     a: 10
        // }
        console.log(a); // 100
        console.log(f); // 123

当代码执行完demo(1)后,预编译最终的内容,也就是AO对象和GO对象里面存放了什么

GO {

            变量

            a: 100

            未声明的变量, 是全局变量

            f: 123

            函数声明

            demo: function () {}

        }

        AO {

            形参

            e: 1

            变量

            b: undefined

            c: function sum() {}

            a: 10

        }

最后,在代码输出时,就直接从对象的属性中对应属性值即可。

 

 

 

  • 12
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值