JS学习之函数表达式(一)声明及递归

一.函数声明
函数表达式是JavaScript中的一个既强大有容易令人困惑的特性。定义函数的方式有两种:一是函数声明,二是函数表达式
函数声明如下,这种定义方法会有函数声明提升,也就是函数的调用可以在函数声明前

      function   sayHello(name){
           console.log('hello',name);        
       }
函数表达式如下,即创建一个函数并将它赋值给变量functionName。这种情况下创建的函数叫做匿名函数,因为function关键字后面没有标识符,匿名函数的name属性是空字符串,在使用前必须先赋值
    var sayHello=function(name){
       console.log('hello',name);
       };

函数有一个非标准的name属性,通过这个属性可以访问到给函数指定的名字。这个属性的值用于等于跟在function关键字后面的标识符
//只在firefox,safari,chrome,opera有效

      alert(functionName.name);

二.函数提升
意思是在执行代码之前会先读取函数声明。理解函数提升的关键,就是理解函数声明与函数表达式之间的区别。例如,执行以下代码的结果可能让人意想不到

      //不要这样做
         if(condition){
              function sayHi(){
                  alert('Hi!');
              }
         }else{
               function sayHi(){
                  alert('yo');
               }
}

表面上看,以上代码表示在condition为true时,使用一个sayHi()的定义;否则,就使用另一个定义。实际上,这在ECMAScript中属于无效语法,JavaScript引擎会尝试修正错误,将其转换为合理的状态。但问题是浏览器尝试修正错误的做法并不一致。大多数浏览器会返回第二个声明,忽略condition;firefox会在condition为true时返回第一个声明。因此这种使用方式很危险,不应该出现在你的代码中,不过,如果是使用函数表达式,那就没有什么问题了。

    //可以这么做
    var  sayHi;
    if(condition){
       sayHi=function(){
           alert('Hi');
       };
    }else{
      sayHi=function(){
        alert('yo');
      };
    }

这个例子不会有什么意外,不同的函数会根据condition被赋值给sayHi。
能够创建函数再赋值给变量,也就能够把函数作为其他函数的值返回。

function createComparisonPunction(propertyName){
    return  function(object1,object2){
           var  value1=object1[propertyName];
           var value2=object2[propertyName];
           if(value1<value2){
                return -1;
           }else if(value1>value2){
                return 1;     
           }else{
                return 0;
           }
    };

}

createComparisonFunction()就返回了一个匿名函数。返回的函数可能会被赋值给一个变量,或者以其他方式被调用;不过,在createComparison()函数内部,它是匿名的。在把函数当成值来使用的情况下,都可以使用匿名函数。
三.递归
递归函数是在一个函数通过名字调用自身的情况下构成的,如下所示。

   function factorial(num){
          if(num<=1){
               return 1;
           }else{
                return num  *factorial(num-1);
             }
       }

这是一个经典的递归阶乘函数。虽然这个函数表面看来没什么问题,但下面的代码却可能导致它出错。

  var anotherFactorial=factorial;
   factorial=null;
   alert(anotherFactorial(4));   //出错

以上代码先把factorial函数保存在变量anotherFactorial中,然后将factorial变量设置为null,结果只想原始函数的引用只剩下一个。单在接下来调用anotherFactorial()时,由于必须执行factorial(),而factorial不再是函数,所以就会导致错误,在这种情况下,使用arguments.callee可以解决这个问题。
我们知道,arguments.callee是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用,例如:

  function factorial(num){
      if(num<=1){
         return 1;
      }else{
         return num *arguments.callee(num-1);
      }
   }

通过使用argument.callee代替函数名,可以确保无论怎样调用函数都不会出问题。因此。在编写递归函数式,使用arguments.callee总比函数名更保险。
但在严格模式下,不能通过脚本访问arguments.callee,访问这个属性会导致错误,不过,可以使用命名函数表达式来达成相同的结果。例如:

    var factorial =(function f(num){
           if(num<=1){
                return 1;
            }else{
                return   num*f(num-1);
            }
        });

以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量factorial。即便把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。

声明:本文章是摘自JavaScript高级程序设计(第3版)第7章。

猜你喜欢

转载自blog.csdn.net/qq_38262910/article/details/82147600