1. 程式人生 > >JS學習之函式表示式(一)宣告及遞迴

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章。