• 吃透【预解释】,从此再也不用担心!


    Author:李金涛 Form:光环国际 Time:2017-12-31 23:49(跨年夜的最后一刻,我在辛勤耕耘我的“预解释”,收获满满,甚喜!)

    定义:预解释(变量提升)js在运行前,先把所有带varfunction关键字的提前声明或定义。且预解释是发生当前作用域下的。

    1,全局预解释阶段:

    1)全局作用域与全局变量:当浏览器加载HTML页面的时候,首先会提供一个供全局JavaScript代码执行的环境,称之为全局作用域(global/ window)window全局作用域下定义的变量-->全局变量,全局都可以调用和修改。在当前作用域中,js代码从上到下执行之前,浏览器会默认的 先把所有带varfunction关键字的进行提前的声明或者定义--->预解释(变量提升)

    2var关键字的和带function关键字的在预解释的时是不一样的
    ①var:预解释的时候只提前的声明,在代码执行的过程中才定义赋值
    ②function:预解释的时候声明+定义都完成了(这也是为什么函数可以把调用写到定义前面的原因。function预解释的时候,会开辟的内存空间,(我们叫堆内存;)并且把function内的代码当做字符串储存在堆内存内。(对象堆内存中,存的是属性和属性值)

    2,代码执行期间:

    ①顺序执行。对var的变量赋值;function(){}跳过(已经预解释过);

    ②若function() 调用,会开辟一个新的内存地址,这个新的空间内存叫栈内存,这时候的的作用域是私有作用域;此时该有三步:形参赋值(无形参跳过);当前作用域下预解释;执行函数。当函数执行完成后,私有作用域一般就会销毁。(特殊:fn()()连续执行不立即销毁;不销毁:函数return返回引用数据类型的值且被全局变量接收(return返回堆内存地址被全局变量占用 var f=fn(); f() );函数中给DOM元素的事件绑定方法(通过DOM方法获取的元素或元素集合都是对象数据类型。函数中方法地址给了DOM元素对象的事件属性。eg:oBtn.onclick=xxff00))

    3,局部预解释:

    1)作用域链:如果在当前作用域下的变量没有预解释,则向它的上一级作用域中找,直到找到window为止,如果window下也没有定义,就会报错;

    2)闭包:函数执行时私有作用域下也要进行预解释,把所有在当前私有作用域下带varfunction的进行提前的声明或者定义;如果是在私有作用域中预解释的了我们称之为私有变量或私有函数;函数中的私有的,函数外面不能直接使用的,我们把这种机制叫做闭包;(局部变量:形参和内部声明的变量)

     

    4,预解释是毫无节操的一种机制

    预解释注意事项:

    1)预解释只发生在在varfunction上。即只能对varfunction进行提前声明或定义,window.a这样的不会进行预解释;

    2)预解释只对当前脚本块起作用,不能跨脚本。但是申明的全局变量,在下个脚本块可以使用。  

    3在预解释重名只声明一次,不重声明,但是要重的定义。(在以后的项目中,切记不要把函数名和变量名相同)

    4js只对等号左边预解释,等号右边是赋值,不会进行预解释(return 后面也是值)。

    5)条件内部只声明不定义。不看条件,见到 var就声明,function也只声明。(条件成立时,顺序执行)

        也就是说,即使条件不成立,里面只要有varfunction也会预声明。

    6)预解释不受return影响,return下一行,会在当前作用域下预解释。

              但return后的为返回值且不执行,相当于"="右边不预解释

    7自执行函数不参与预解释

     

    下面是我的demo代码:

     //预解释规律:
    //预解释是毫无节操的一种机制;
    //1,不看条件,见到 var就声明,function也只声明;
    //2,预解释只管"="左边,"="右边是值 不参与预解释
    //3,自执行函数不参与预解释
    //4,return后的为返回值且不执行,相当于"="右边不预解释;但是return下一行,会在当前作用域下预解释。
    //5,预解释,重名只会声明一次,但会不断赋新值;(JS很懒很精明,不会做重复的事)
    //6.预解释只发生在同一个脚本块,不能跨脚本
       // 7,预解释只发生在在var和function上。window.a这样的不会进行预解释;
    //    console.log(num);//undefined
    // if(!("num" in window)){
    // var num=10;
    // }
    // console.log(num);//undefined

    // console.log(f1);//undefined
    // if(!(f1 in window)){
    // function f1() {//条件内部,函数只声明不定义
    // console.log("条件内部函数体");
    // }
    // }
    // console.log(f1);//undefined
    // console.log(f1());// TypeError: f1 is not a function

    // console.log(f1);//undefined
    // if(1==1){
    // function f1() {//条件内部,函数只声明不定义
    // console.log("条件内部函数体");
    // }
    // }
    // console.log(f1);//函数体
    // console.log(f1());//条件内部函数体 undefined


    // console.log(f12);//正常函数体
    // console.log(f12());//正常函数体 undefined
    // function f12() {
    // console.log("正常函数体");//undefined
    // }
    // console.log(f12());//正常函数体 undefined
    //
    // function f11() {
    // num1=1;
    // console.log(num1);
    // }
    // console.log(f11);//函数体
    // console.log(f11());//1 undefined

    // console.log(a);//undefined
    // if(1==1){
    // var a=12;
    // }
    // console.log(a);//12

    // console.log(a);//undefined
    // if(1==2){
    // var a=12;
    // }
    // console.log(a);//undefined


    //2,预解释只管"="左边,"="右边是值 不参与预解释
    //// console.log(num);//ReferenceError: num is not defined
    // var f2=function () {
    // var num=2;
    // };
    // console.log(num);//ReferenceError: num is not defined
    // f21();//TypeError: f21 is not a function
    // var f21=function () {
    // console.log("ok");
    // };
    // f21();//ok 正常调用匿名函数


    //3,自执行函数不参与预解释
    // console.log(num);//num is not defined
    // (function (num) {console.log(num+1)})(100);
    // ~function (num) {console.log(num+1)}(100);
    // (function (num) {console.log(num+1)})(100);
    // console.log(num);//num is not defined;num是形参,是局部变量;


    //4,return后的为返回值且不执行,相当于"="右边不预解释;但是return下一行,会在当前作用域下预解释。
    // function f4() {
    // console.log(num);//undefined
    //// f41();//f41 is not defined
    // f42();//okf42 f42函数已经预解释
    // return function f41() {
    // console.log("ok");
    // };
    // var num=4;
    // function f42() {
    // console.log("okf42");
    // };
    // console.log(num);//return后不执行,没有结果
    // }
    // f4();
    // console.log(num);//num is not defined

    //5,预解释,重名只会声明一次,但会不断赋新值;(JS很懒很精明,不会做重复的事)
    // console.log(f5);//ƒ f5() {console.log("52")}
    // f5();//52
    // function f5() {console.log("51")};
    // f5();//52
    // var f5=50;
    // console.log(f5);//50
    // f5();//52() TypeError: f5 is not a function
    // function f5() {console.log("52")};
    // f5();//报错后不在执行,JS是解释性语言,边读边解释,遇错即停止。

    </script>
    <!--&lt;!&ndash;6.预解释只发生在同一个脚本块,不能跨脚本&ndash;&gt;-->
    <!--<script>-->
    <!--console.log(a);//预解释只发生在同一个脚本快,不能夸脚本;//所以此时a is not defined-->
    <!--</script>-->

    <!--<script>-->
    <!--var a=13;-->
    <!--</script>-->
    <script>
    // var a=13;
    // var a;
    </script>
    <script>
    // console.log(a);//这个时候是弹出来13、以后写代码,不同代码块的顺序一定要注意;因为第一个脚本快已经赋值了

     (代码块的值是可以用的。这时候已经过了预解释阶段了,是定义过的了)

    </script>
    <script>

    // function f() {
    // return
    // }
    // console.log(f);
    // console.log(f());//undefined 没值跟没有return一样

    // console.log(a);
    // var a=10;
    // console.log(a);

    //window.a这样的不会进行预解释
    // console.log(a);//a is not defined
    // window.a=10;
    // console.log(a);
    </script>

    下面是一个预解释面试题;

    <script>

        console.log(a);

        var a=12;

        function sum(){

            console.log(a);

            var a=13;

            console.log(a);

        }

        sum();

        console.log(a);

        //答案是:undefinedundefined1312

    </script>

    原理分析

    ————

    预解释是毫无节操的一种机制;预解释完成;代码从上到下加载;分别给变量定义赋值(带var的情况;);如果遇到function sum(){}定义的这个部分,直接跳出即可;如果遇到的是函数的执行,则进行下面的处理;

    开辟一个新的私有作用域(栈内存)、用来执行函数中的js代码;

    在执行代码前

    思考:闭包内不带var的变量赋值,和全局情况下不带var的变量赋值是什么流程?

    预解释是发生在当前作用域下的;

    预解释的知识点:

    如果判断他的上一级作用域;

    看存储这个函数的堆内存在哪一个作用域(A)下,那么这个函数的执行的上一级作用域就是A;(有些时候函数A内套函数B,套函数C,而C可能是在全局作用域内拿出来运行的,这个他的作用域是谁??)

    如何判断是私有的还是全局的;

    在一个函数中只有预解释声明过的和形参是定义过的,是私有的;否则往他的上一级作用域找,如果上一级作用域也没有,则继续找;一直找到window为止;

    题目修改:去掉闭包内的一个var

    <script>

        console.log(a);

        var a=12;

        function sum(){

            console.log(a);

            a=13;//没有var;没有预解释过,也不是形参。所以不是私有变量的;要往上一层找,一直找到window为止;

            console.log(a);

        }

        sum();

        console.log(a);

        //答案是:undefined121312

    </script>

    得到的结果是完全不一样的;

    再次修改:两个var都去掉;

    <script>

        console.log(a);//此时是没有预解释过a的,a就是没有定义的;会报错; a is not undefined

        a=12;//也没有vara is not defined;下面代码会都不执行了。前提是没有进行异常捕获处理;

        function sum(){

            console.log(a);

            a=13;//没有var;没有预解释过,也不是形参。所以不是私有变量的;要往上一层找,一直找到window为止;

            console.log(a);

        }

        sum();

        console.log(a);

        //答案是:Uncaught ReferenceError: a is not defined;;就是a is not defined;其它都没有了。

    </script>

    这时候就会报错了;

    如果再再次修改:注释掉第一行;

    //console.log(a);

    答案是

    <script>

        //console.log(a);

        a=12;//也没有vara is not defined;下面代码会都不执行了。前提是没有进行异常捕获处理;

        function sum(){

            console.log(a);

            a=13;//没有var;没有预解释过,也不是形参。所以不是私有变量的;要往上一层找,一直找到window为止;

            console.log(a);

        }

        sum();

        console.log(a);

        //答案是:12/13/13

    </script>

    var不带var的区别;

    全局作用域下才有下面这种一个机制的;

    var a=12;//在全局作用域下定义了一个变量a=12,相当于给window增加一个属性名a;属性值是12

    a=12//就是给window增加了一个属性名a,值是12;(严格模式下是会报错的;)

    区别:上面的预解释了,下面是没有预解释;

    再再在次修改是:

    <script>

        //console.log(a);

        //a=12;

        function sum(){

            console.log(a);

            a=13;

            console.log(a);

        }

        sum();

        console.log(a);

        //答案是:Uncaught ReferenceError: a is not defined

    </script>

    再再再次修改:

    <script>

        //console.log(a);

        //a=12;

        function sum(){

            //console.log(a);

            a=13;//当前这种情况是属于给window增加一个属性值;属性值是13

            console.log(a);

        }

        sum();

        console.log(a);

        //答案是:13,13

    </script>

    ——————–

    面试题思路;

    <script>

        var a=12;

        function fn(){

            var a=13;

            function ff(){

                a++;

                console.log(a);

            }

            return ff;

        }

        var f=fn();

        f();

        console.log(a);

        //答案:14/12

        // ff是存在fn中的;ff里面的a是私有的。向上级找,也就是fn里面;

    </script>

    虽然f()在外面执行,但是他的上级作用域还是fn(),a是要在上级作用域里找的;

    ————

    预解释是毫无节操的一种机制;

    <script>

        if(!("a" in window)){

            var a=12;

        }

        console.log(a);//结果是undefined,而不是报错。

    </script>

    1、预解释是发生在当前作用域下的;

    2、如果有if判断;不管条件是否成立,都要预定义;

    上面题目的分析;

    规定:var a=12;相当于给window增加一个属性名,属性值是12

    in:用来检测某一个属性名是否是这个对象的;”a”in window 的意思是:判断window下有”a“这个属性名;属于的话返回true,不属于返回false

    预设值的时候;if判断里面的a是会被预解释的,相当于给window加一个属性名;

    执行代码;if(!(“a” in window))。这个条件是false,所以不继续执行。

    这个时候console.log(a)。这时候a已经被预解释了,只是a没有定义,是undefined、所以结果是undefined;(并非报错的那种a is undefined)【分析完成】

    3、预解释值发生在=左边的,右边的不进行预解释;

    <script>

    function fn(){};//这个是function声明+定义的;

        var fn=function(){}//fn预解释,只声明了fn的变量;

        //虽然两个一样的用法;但是预解释不一样的;

    </script>

    第二个例子:

    <script>

       fn();// fn is not a function

        var fn=function(){}

        //fn();

    </script>

    如果改成下面,就不会报错;会执行fn()

    <script>

       //fn();// fn is not a function

        var fn=function(){}

        fn();

    </script>

    就是下面这样的;

    <script>

       //fn();// fn is not a function

        var fn=function(){

            console.log("123")

        }

        fn();

        //控制台是显示123

    </script>

    接上面的继续总结;

    4、只有预解释发生在同一个脚本快,不能夸脚本

    <body>

    <script>

        console.log(a);//预解释只发生在同一个脚本快,不能夸脚本;//所以此时a is not defined

    </script>

    <script>

        var a=13;

    </script>

    </body>

    <body>

    <script>

        var a=13;

    </script>

    <script>

        console.log(a);//这个时候是弹出来13、以后写代码,不同代码块的顺序一定要注意;因为第一个脚本快已经赋值了(代码块的值是可以用的。这时候已经过了预解释阶段了,是定义过的了)

    </script>

    </body>

    5、自执行函数不进行预解释   

    ;(function(){})();//自执行函数不解析预解释

    6、函数中,return后面的代码也要进行预解释;

    7、在预解释的时候,如果发现名字重了,不重新的声明,但是要重新的定义;

    function a(){

    //变量的名a和函数名a,如果重名后也是冲突的;

    }

    </script>

    下面是一个经典的思路,一个不错的面试题:

    <script>

        fn();

        var fn=13;

        fn();

        function fn(){

            console.log(100);

        }

        fn();

        //答案:100fn is not a function(只有函数才能执行;fn()相当于13(),这个是不能执行的;)、后面的不执行了

    </script>

    留意:报错的fn is not a function也有浏览器弹出 number is not  function

    练习做题;

    <script>

        console.log(fn);

        function fn(){

            console.log(1);

        }

        console.log(fn);

        var fn;

        console.log(fn);

        function fn(){

            console.log(2);

        }

        console.log(fn);

        var fn=10;

        console.log(fn);

    </script>

     

    
    
  • 相关阅读:
    <译>Spark Sreaming 编程指南
    <译>Zookeeper官方文档
    <译>Flink官方文档-Flink概述
    <译>流计算容错
    <译>Flink编程指南
    <续>调度算法补充
    storm源码阅读笔记之任务调度算法
    海量数据处理方法归类
    storm中worker、executor、task之间的关系
    javax.swing.jFrame
  • 原文地址:https://www.cnblogs.com/ljt1412451704/p/8159386.html
Copyright © 2020-2023  润新知