函式宣告和函數運算式——函式宣告的聲明提前

來源:互聯網
上載者:User

定義函數的方法

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

    函式宣告(Function Declaration)
    函數運算式Function Expression)
    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。

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

    變數聲明會提前到函數的頂部;
    只是聲明被提前,初始化不提前,初始化還在原來初始化的位置進行初始化;
    在聲明之前變數的值是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(好殘酷的現實。。。)。



相關文章

Alibaba Cloud 10 Year Anniversary

With You, We are Shaping a Digital World, 2009-2019

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。