1. 程式人生 > >函式宣告和函式表示式區別

函式宣告和函式表示式區別

定義函式的方法

定義函式的方法主要有三種:

  1. 函式宣告(Function Declaration)
  2. 函式表示式Function Expression)
  3. new Function建構函式

其中,經常使用的是函式宣告和函式表示式的函式定義方法,這兩種方法有著很微妙的區別和聯絡,而且這兩種方法的使用也容易混淆,所以這篇文章主要總結下這兩種函式定義方法的相關知識點,當然本文的主題依然是關於函式提前的。

函式宣告的典型格式:

function functionName(arg1, arg2, ...){
    <!-- function body -->
}

函式表示式

  • 函式表示式的典型格式:

    var  variable=function(arg1, arg2, ...){
                <!-- function body -->
    }
  • 包含名稱(括弧,函式名)的函式表示式:

    var  variable=function functionName(arg1, arg2, ...){
            <!-- function body -->
    }

    像上面的帶有名稱的函式表示式可以用來遞迴:

    var  variable=function functionName(x){
            if(x<=1)
                return
    1
    ; else return x*functionName(x); }

宣告提前

var宣告提前

小夥伴們應該都聽說過宣告提前的說法,我想在此再次重申一遍,因為宣告提前是函式宣告和函式表示式的一個重要區別,對於我們進一步理解這兩種函式定義方法有著重要的意義。

但是再說函式宣告提前之前呢,有必要說一下var宣告提前

先給出var宣告提前的結論:

變數在宣告它們的指令碼或函式中都是有定義的,變數宣告語句會被提前到指令碼或函式的頂部。但是,變數初始化的操作還是在原來var語句的位置執行,在宣告語句之前變數的值是undefined。

上面的結論中可以總結出三個簡單的點:

  1. 變數宣告會提前到函式的頂部;
  2. 只是宣告被提前,初始化不提前,初始化還在原來初始化的位置進行初始化;
  3. 在宣告之前變數的值是undefined。

還是來例子實在:

var handsome='handsome';
function handsomeToUgly(){
    alert(handsome);
    var handsome='ugly';
    alert(handsome);
}
handsomeToUgly();

正確的輸出結果是:
先輸出undefined,然後輸出ugly

錯誤的輸出結果是:
先輸出handsome,然後輸出ugly

這裡正是變數宣告提前起到的作用。該handsome區域性變數在整個函式體內都是有定義的,在函式體內的handsome變數壓住了,哦不對,是覆蓋住了同名的handsome全域性變數,因為變數宣告提前,即var handsome被提前至函式的頂部,就是這個樣子:

var handsome='handsome';
function handsomeToUgly(){
    var handsome;
    alert(handsome);
    var handsome='ugly';
    alert(handsome);
}
handsomeToUgly();

所以說在alert(handsome)之前,已經有了var handsome宣告,由上面提到的

在宣告之前變數的值是undefined

所以第一個輸出undefined

又因為上面提到的:

只是宣告被提前,初始化不提前,初始化還在原來初始化的位置進行初始化

所以第二個輸出ugly

函式宣告提前

接下倆我們結合var宣告提前開始聊函式宣告的宣告提前

函式宣告的宣告提前小夥伴們應該很熟悉,舉個再熟悉不過的例子。

sayTruth();<!-- 函式宣告 -->
function sayTruth(){
    alert('myvin is handsome.');
}

sayTruth();<!-- 函式表示式 -->
var sayTruth=function(){
    alert('myvin is handsome.');
}

小夥伴們都知道,對於函式宣告的函式定義方法,即上面的第一種函式呼叫方法是正確的,可以輸出myvin is handsome.的真理,因為函式呼叫語句可以放在函式宣告之後。而對於函式表示式的函式定義方法,即上面的第二種函式呼叫的方法是不能輸出myvin is handsome.的正確結果的。

結合上面的myvin is handsome.例子,函式宣告提前的結論似乎很好理解,不就是在使用函式宣告的函式定義方法的時候,函式呼叫可以放在任意位置嘛。對啊,你說的很對啊,小夥伴,我都不知道怎麼反駁你了。那就容我再扯幾句。

從小夥伴所說的

不就是在使用函式宣告的函式定義方法的時候,函式呼叫可以放在任意位置嘛

可以引出一點:

函式宣告提前的時候,函式宣告和函式體均提前了。

而且:

函式宣告是在預執行期執行的,就是說函式宣告是在瀏覽器準備執行程式碼的時候執行的。因為函式宣告在預執行期被執行,所以到了執行期,函式宣告就不再執行(人家都執行過了自然就不再執行了)。

上面是一點。

函式表示式為什麼不能宣告提前

我們再說一點:為什麼函式表示式不能像函式宣告那樣進行函式宣告提前呢?

辛虧我知道一點兒,否則真不知道我該怎麼回答呢?

咳咳,按照我的理解給小夥伴們解釋一下下:

我們上面說了var的宣告提前,注意我上面提過的:

只是宣告被提前,初始化不提前,初始化還在原來初始化的位置進行初始化

Ok,我們把函式表示式擺在這看看:

var  variable=function(arg1, arg2, ...){
                    <!-- function body -->
}

函式表示式就是把函式定義的方式寫成表示式的方式(貌似是白說,但是這對於解釋和理解為毛函式表示式不能函式宣告提前具有良好的療效),就是把一個函式物件賦值給一個變數,所以我們把函式表示式寫成這個樣子:

var varible=5

看到這,也許小夥伴們會明白了,一個是把一個值賦值給一個變數,一個是把函式物件賦值給一個變數,所以對於函式表示式,變數賦值是不會提前的,即function(arg1, arg2, ...){<!-- function body -->}是不會提前的,所以函式定義並沒有被執行,所以函式表示式不能像函式宣告那樣進行函式宣告提前。

函式宣告提前的例項分析

還是那句話,還是例子來的實在:

sayTruth();
if(1){
    function sayTruth(){alert('myvin is handsome')};
}
else{
    function sayTruth(){alert('myvin is ugly')};
}

在瀏覽器不丟擲錯誤的情況下(請自行測試相應的瀏覽器是否有丟擲錯誤的情況,為啥我不測試?我能說我懶麼。。。),瀏覽器的輸出結果是輸出myvin is ugly(我不願承認,但是事實就是這樣啊啊啊啊,難道道出了人醜就該多讀書??????)。

為什麼呢?當然是宣告提前了。因為函式宣告提前,所以函式宣告會在程式碼執行前進行解析,執行順序是這樣的,先解析function sayTruth(){alert('myvin is handsome')},在解析function sayTruth(){alert('myvin is ugly')},覆蓋了前面的函式宣告,當我們呼叫sayTruth()函式的時候,也就是到了程式碼執行期間,宣告會被忽略,所以自然會輸出myvin is ugly(好殘酷的現實。。。)。忘了的可以看上面說過的:

函式宣告是在預執行期執行的,就是說函式宣告是在瀏覽器準備執行程式碼的時候執行的。因為函式宣告在預執行期被執行,所以到了執行期,函式宣告就不再執行了(人家都執行過了自然就不再執行了)。

小了個結

關於函式宣告的函式提前(提升)就聊到這裡先,希望我的理解和扯淡能夠對有需要的小夥伴有所幫助