美文网首页
05 -- 递归、预编译

05 -- 递归、预编译

作者: 耦耦 | 来源:发表于2018-01-30 01:25 被阅读11次

    小练习1:N的阶乘

    //n的阶乘一般求法:
    function mul(n){
        var num = 1;
        for(var i = 1;i<=n; i++){
           num *=i;
        }
        return num;
    }
    
    mul(5)
    
    //递归:
    //规律n! = n * (n - 1);
    function mul(n){
        if(n == 1 ||  n==0){
            return 1;
        }
        return n * mul(n-1);
    }
    
    mul(5);
    

    递归需要注意的两点:1.找规律、2.找出口。没有出口的话递归就会无限死循环,所以必须用一个已知的条件当做它的出口。递归只有一个好处让代码变得简洁,除此之外无任何好处。递归的速度特别慢(因为每一个结果都要等待下一次递归执行的结果), 所以特别复杂的程序不能用递归。

    小练习2:菲波那切数列

    //规律 fb(n) = fb(n-1) + fb(n- 2)
    function fb(n){
        if(n ==1 || n ==2){
            return 1;
        }
        return fb(n) = fb(n-1) + fb(n- 2);
    }
    

    递归最典型的就是这两个:阶乘和菲波那切数列。有规律有出口就可以大胆的使用递归。


    作用域初探

    1、作用域定义:变量(变量作用域又称上下文)和函数生效(能被访问)的区域。

    function test(){
        var a = 123;
        function demo(){
            var b = 234;
            document.write(a);
        }
        demo();
        document.write(b);
    }
    test();
    

    结果:123、undefined
    互相嵌套的函数,外层的函数不能访问里层的变量,但里层的函数可以访问外层的变量,也就是越往里它的权限就越大。

    2、全局、局部变量
    定义在全局的变量称为全局变量,定义在函数内部的变量称为局部变量。

    3、作用域的访问顺序

    js运行三部曲

    1、语法分析
    javascript的两大特点,单线程和解释性语言。js在执行的时候是解释一行执行一行而不是通篇编译再执行,但在执行之前会先通篇扫描一下有没有低级的语法错误,比如多个大括号,有中文符号等等,但不执行。这个扫描过程就叫语法分析的过程。 然后进行预编译,最后在再解释执行。

    2、预编译

    对于预编译,记住两句话:

    • 函数声明整体提升,也就是只要你写了一个函数声明,无论写在那里,系统都会把函数提到逻辑的最前面、所以不管在那里调用它,本质上都是在函数下面调用;
    • 变量 声明提升,变量一旦声明,无论是否赋值,变量的声明都会被提升到逻辑的最前面,但仅仅是声明被提升了,变量的赋值并不被提升。

    但是这两句话解决不了所有的问题,来看一下下面这个:

    console.log(a);
    function a(a){
        var a = 234;
        var a = function(){  
        }
        a()
    }
    var a = 123;
    

    函数名和变量名重了要怎么办呢?这就是那两句话不能解决的问题。学完预编译,所有的问题都会解决。

    预编译前奏

    • 1.imply global 暗示全局变量:即任何变量,如果变量未经声明及赋值,此变量就为全局对象(window)所有。全局上的任何变量,即使声明了也归window所有。
      • eg: a = 123;
      • eg: var a = b = 123; (注意连续 赋值是自右向左的)
    //window就是全局的域
    var a = 123;
    //相当于在window里添加
    //window{
    //    a:123;
    //}
    //如果变量定义在全局上,那就相当于在全局上挂了一个这样的属性。
    //下一次在访问这个a的时候,会在windows里面去找是否有这个a。
    
    • 2.一切声明的全局变量,全是window的属性。

      • eg: var a = 123; ===> window.a = 123;
    • 3.window就是全局。

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

    既有变量又有函数,都存在提升,也会存在一个覆盖的问题,那么这里任何提升?谁覆盖谁呢?这就是预编译发生的奇妙的过程。

    预编译发生在函数执行的前一刻。预编译经过如下四个过程:

    预编译四部曲

    • 1.创建AO对象:Activation Object(执行期上下文)
    AO{
        //AO对象
    }
    
    • 2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined。这里讲的是函数的,全局比较简单。
    AO{
        a: undefined;
        b: undefined;
    }
    
    • 3.将实参和形参统一
    AO{
        a: 1;
        b: undefined;
    }
    
    • 4.在函数体里面找函数声明,值赋予函数体(注意是函数声明而不是函数表达式
    AO{
        a: function d(){};
        b: undefined;
        d: function d(){}
    }
    

    预编译四部曲的第四部优先级最高。到这里预编译基本完成,开始解释执行。执行期上下文相当于作用域。
    3、解释执行
    解释执行时,变量的值是在AO里面找的,如上面第四步所示。

    function fn(a){
        console.log(a);       //function a(){}
    
        var a = 123;  //预编译时,变量的声明已经被提升,
        //所以这里未执行的只有a=123这个赋值语句,
        //也就是相当于重新给a赋值,覆盖了AO里面的原始的a的值,
        //到这里,a的值为123,而不是function...
    
        console.log(a);       //123 
    
        function a(){};   //解释执行时,预编译看过的地方不用再看。
    
        console.log(a);      //123
    
        var b = function(){};   //b变成function(){} ==>  b: function(){}
    
        console.log(b);     //function(){}
    
        function d(){}
    }
    
    fn(1);
    

    所以最后的执行结果是:

    function a(){}
    123
    123
    function (){}
    

    再看一个例子:

    function test(a, b){
        document.write(a);
        c = 0;
        var c;
        a = 3;
        b = 2;
        document.write(b);
        function b(){};
        function d(){};
        document.write(b);
    }
    test(1);
    

    经过预编译四部曲后的AO:

    //第一、二步:
    AO{
        a: undefined;
        b: undefined;
        c: undefined;
    }
    //第三步:
    AO{
        a: 1;
        b: undefined;
        c: undefined;
    }
    //第四步:
    AO{
        a: 1;
        b: function b(){};
        c: undefined;
        d: function(){};
    }
    //最后执行后的结果为:
    //1
    //2
    //2
    

    再举几个栗子:

    function test(a, b){
        console.log(a);
        console.log(b);
        var b = 234;
        console.log(b);
        a = 123;
        console.log(a);
        function a(){};
        var a;
        b = 234;
        var b = function(){};
        console.log(a);
        console.log(b);
    }
    test(1)
    
    //AO
    AO{
        a: function a(){};
        b: undefined;
    }
    

    结果:

    function a(){}
    undefined
    234
    123
    123
    function(){}
    

    预编译不止发生在函数里,还发生在全局。来看一下全局的预编译:

    • 1.生成一个GO 对象 Global Object。GO===window。
      其他是一样的,不过全局没有参数。
    console.log(window.a) === console.log(GO.a) 
    也就相当于console.log(a)
    
    function test(){
        var a = b = 123;
        console.log(window.b);    //123
        console.log(window.a);    //undefined
    }
    test();
    
    GO{
        b: 123;   //未经声明的变量归GO所有。
    }
    
    AO{
        a: undefined;
    }
    

    AO和GO,先生成GO;

    最后看一个特别恶心的栗子:

    GO{
        test: function(){
            //...
        }
    }
    
    console.log(test);    //   结果 ==> function test(){...}
    function test(test){
        console.log(test);    // 结果 ==> function test(){//函数内部这个 }
        //这里GO和AO里面都有test,先找AO里面的值,
        //也就是遵循一个我自己有就用自己的,我没有才用别人的原则。
        var test = 234;
        console.log(test);     //234
        function test(){
        }
    }
    
    AO{
        // 先是  test: undefiend;
        test: function test(){
        },
    }
    
    test(1);
    var test = 123;
    

    再看一个:

    //Go{
    // a: undefiend;
    // c: 234;    暗示全局变量
    //}
    
    function test(){
        console.log(b);    // undefined
        if(a){    //注意,找变量声明和形参的时候不执行if或for语句,直接把里面的东西拿出来
            var b = 100;
        }
        console.log(b);    // undefined
        c = 234;
        console.log(c);    //234
    }
    
    var a;
    test();
    //AO{
    //    b: undefiend;
    //}
    a = 10;
    console.log(a);    // 10
    console.log(c);     //234
    

    13年百度面试题1:

    function bar(){
        return foo;
        foo = 10;
        function foo(){
        }
        var foo = 11;
    }
    console.log(bar());     // function foo(){}
    

    13年百度面试题2:

    console.log(bar());        //11
    function bar(){
        foo = 10;
        function foo(){
           
        }
        var foo = 11;
        return foo;
    }
    

    练习1:

    a = 100;
    function demo(e){
        function e(){  };
        arguments[0] = 2;
        console.log(e);    //2
        if(a){
            var b = 123;
             function c(){
                  //以前可以在if里面定义function,现在不允许了
             }
        }
        var c;
        a = 10;
        var a;
        console.log(b);    //undefined
        f = 123;
        console.log(c);    //function (){ }
        console.log(a);     //10
    }
    var a ;
    demo(1);
    console.log(a);   //100
    console.log(f);    //123
    

    练习2:

    var str = false + 1;     
    console.log(str);    // 1
    var demo = false == 1;
    conso.log(demo);   // false
    if(typeof(a) &&-true + (+undefined) + ""){
        //  typeof(a)  ==> 字符串型的undefined
        //-true + (+undefined) + ""  ==> NaN
        console.log("基础扎实");
    }
    if(11 + "11" * 2 == 33){
        console.log("基础扎实);
    }
    !!" " + !!"" - false||console.log("你觉得能打印,你就是猪");
    // 空格字符串是ture,!!就能把它变成布尔值
    //空字符串是false,!!就能把它变成布尔值
    

    typeof()是唯一一个使用未定义的变量不报错,返回undefined的方法。typeof(null)返回对象。一般数学符号的隐式转换,都是转换为number,转换不了的一般都是NaN。

    练习3:css中display的属性有几种?

    none    隐藏元素
    block    块级元素
    inline   行内元素
    inline-block    
    

    练习4:window.foo的值为?

    (window.foo || (window.foo = 'bar'));
    //   ||的优先级高于=
    // bar(先读括号)
    

    相关文章

      网友评论

          本文标题:05 -- 递归、预编译

          本文链接:https://www.haomeiwen.com/subject/acufzxtx.html