05 -- 递归、预编译

2018-01-30  本文已影响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;

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

预编译前奏

//window就是全局的域
var a = 123;
//相当于在window里添加
//window{
//    a:123;
//}
//如果变量定义在全局上,那就相当于在全局上挂了一个这样的属性。
//下一次在访问这个a的时候,会在windows里面去找是否有这个a。
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);

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

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

预编译四部曲

AO{
    //AO对象
}
AO{
    a: undefined;
    b: undefined;
}
AO{
    a: 1;
    b: undefined;
}
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(){}

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

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(先读括号)
上一篇下一篇

猜你喜欢

热点阅读