js预编译详解----分分钟了解预编译

在JavaScript中存在一种预编译的机制,这也是Java等一些语言中没有的特性,也就正是因为这个预编译的机制,导致了js中变量提升的一些问题

  1. 函数声明整体提升
  2. 变量声明提升

众所周知,在js中undefined通常用于指示变量尚未赋值。对未定义值的引用返回特殊值 undefined。
而我们也知道js属于解释型语言即解释一句执行一句。

    <script>
        console.log(a)
        var a = 10;
        console.log(b);
    </script>

按照其定义来说我们是先控制台输出a,再进行声明变量和赋值
在这里插入图片描述
我们不难得出,如果我们不对一个变量进行声明与赋值直接输出,控制台会报错,而一开始的a却没有报错,这就是上面所说的声明变量提升。即将变量var提升至script代码块最上方。所以,代码应该是这样的:

    <script>
        var a;
        console.log(a)
        a = 10;
        console.log(b);
    </script>

这就是为什么两个都没在输出前进行申名与赋值,但是输出结果却不一样。
从这里我们就不难理解函数的声明整体提升。即声明与代码块一起提升至代码块最上方。

JS执行过程

  1. 语法检测:通篇扫描语法错误
  2. 预编译
  3. 代码执行:逐行执行

预编译前奏

  • imply global暗示全局变量,任何变量,如果变量未经声明就赋值,这些变量就为全局对象所有。
  • 一切声明的全局变量和未经声明的变量,全归window所有。

变量分为局部变量和全局变量;而他俩的区别就是作用域不同:

  • 全局变量的作用域为整个程序
  • 而局部变量的作用域为当前函数或循环等。
    <script>
        function fn (){
            a=0
            console.log(a);
        }
        fn();
        console.log(a);
    </script>

这里就可以知道在函数fn里的变量为局部,而在外面就是全局。
如果你这么认为就错了,上面说了任何变量,如果变量未经声明就赋值,这些变量就为全局对象所有。
这个a看样子是写在函数里面的,应该算局部变量,但是他并没有声明,所以,他属于全局变量。所以,这两个在函数里和函数外输出的对象是同一个。

全局预编译

  1. 创建一个函数的GO对象(Global Object),执行期上下文对象
  2. 将var关键字声明的变量,成为GO对象的属性,值为undefined,遇到重名,不做任何变化
  3. 将function声明的函数(函数声明)成为GO对象的属性,值为函数体,重名直接覆盖

函数预编译

从函数执行的前一刻开始:
  1. 创建一个函数的AO对象(Activation Object),执行期上下文对象
  2. 函数的形参,成为AO对象的属性,值为实参的值,若未传值,值为undefined
  3. 将var关键字声明的变量,成为AO对象的属性,值为undefined,遇到重名,不做任何变化
  4. 将function声明的函数(函数声明)成为AO对象的属性,值为函数体,重名直接覆盖
    <script>
        function fn (a){
            console.log(a);
            console.log(b);
            var b = 1;
            console.log(b);
            console.log(c);
            var c = function(){};
            console.log(c);
        }
        fn(1);
    </script>

综上所述,在函数声明开始是,创建一个上下文对象AO = {};

  1. 形参a,实参1;AO = {a:1};
  2. var b,声明b;AO = {a:1,b:undefined};
  3. 函数c;AO = {a:1,b:undefined,c:function(){}};
    注:将function声明的函数(函数声明)成为AO对象的属性,值为函数体,重名直接覆盖
    虽然这是函数表达式,但是,但是,但是:function声明必须是function c (){};才算;像某些爱偷懒的编程猿+function c (){}也不行…
    所以AO最后应该是{a:1,b:undefined,c:undefined},所以,输出应该是1,undefined,1,undefined, ƒ (){}
    全局预编译与函数预编译差不多,唯一的区别就是全局没有函数的实参形参。

案例详解

    <script>
        console.log(b);
        var b = "boy";
        console.log(b);

        function fighting() {

            console.log(a);
            console.log(c);
            if (a === "apple") {
                a = "Alice"
            } else {
                a = "Ada"
            }
            console.log(a)
            var a = "Andy";
            middle();

            function middle() {
                console.log(c++);
                var c = 100;
                console.log(++c);
                small();

                function small() {
                    console.log(a)
                }
            }
            var c = a = 88;

            function bottom() {

                console.log(this.b);
                b = "baby";
                console.log(b)
            }
            bottom();

        }

        fighting();
        console.log(b) 
    </script>
  1. 首先,建立GO对象,看见声明变量b,所以GOb属性,值为undefined;
  2. 然后fighting属性,值为函数。遇见函数执行,建立fighting函数的AO对象
  3. var a,AOa属性,值为undefined;middle属性,值为function middle(){};
  4. 遇见middle执行,建立middleAO对象;
  5. var c,small function 同理
  6. 之后回到fighting函数继续,发现var c,值为undefined;之后bottom,值为function
  7. 遇见bottom执行,创建其AO对象,这里b未声明,则为GO对象属性
  8. 编译结束之后开始逐行执行代码
  9. 在代码块中输出b,即输出全局中的b,即一开始声明是给的值undefined
  10. 接下来赋值“boy”,那么输出就是“boy”;
  11. 函数fighting里输出a,c前面没有赋值,那么从fightingAO对象找对应的属性值,为undefined;
  12. 后面判断,a为undefined,不等于apple,则赋值为“Andy”并输出
  13. middle里c为undefined,不能自增,所以c++为NaN
  14. 后面赋值一百在自增输出,就为101
  15. 函数small输出a,small没有,就向middle要,middle没有就找fighting,fighting还没有就只能找window,
  16. 注:只能儿子找父级,父级找儿子。。。嘿嘿
  17. 所以输出之前的a“Andy”
  18. 88赋值给a,a的值赋值给c
  19. bottom函数输出this.b,虽然强调要this的b,但是没办法,没有就是没有,方法总比困难多,你找我我没,只能找父亲,那么b为先前的“boy”
  20. 赋值“baby”并输出,那么b为“baby”
  21. 最后结束fighting之后输出b,b为“baby”
    这里为啥是baby而不是boy呢?
    变量里面赋值,变量外面输出是输出不了的,因为父亲不好意思找子级要,但是,这里b未声明,所以b是属于window的,所以本来就是父亲的,虽然在子级里面赋值变化了,但是b是父亲的,虽然子级里面改变了但输出还是输出“baby”
    输出结果:
    在这里插入图片描述
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值