JavaScript中的函式宣告和函數運算式區別淺析_javascript技巧

來源:互聯網
上載者:User

記得在面試騰訊實習生的時候,面試官問了我這樣一道問題。

複製代碼 代碼如下:

//下述兩種聲明方式有什麼不同
 
function foo(){};
 
var bar = function foo(){}; 

當初只知道兩種聲明方式一個是函式宣告一個是函數運算式,具體有什麼不同沒能說得很好。最近正好看到這方面的書籍,就想好好總結一番。

  在ECMAScript中,有兩個最常用的建立函數對象的方法,即使用函數運算式或者使用函式宣告。對此,ECMAScript規範明確了一點,即是,即函式宣告 必須始終帶有一個標識符(Identifier),也就是我們所說的函數名,而函數運算式則可以省略。

函式宣告:

複製代碼 代碼如下:

function Identifier ( FormalParameterList opt){ FunctionBody }

函式宣告解析過程如下:

  1. 建立一個new Function對象,FormalParameterList指定參數,FunctionBody指定函數體。將當前正在運行環境中範圍鏈作為它的範圍。

  2. 為當前變數對象建立一個名為Identifier的屬性,值為Result(1)。

函數運算式:

  (函數運算式分為匿名和具名函數運算式)

複製代碼 代碼如下:

  function Identifier opt( FormalParameterList opt){ FunctionBody }  //這裡是具名函數運算式

具名函數運算式的解析過程如下:

1. 建立一個new Object對象
2. 將Result(1)添加到範圍鏈的頂端
3. 建立一個new Function對象,FormalParameterList指定參數,FunctionBody指定函數體。將當前正在啟動並執行執行環境中範圍鏈作為它的範圍。
4. 為Result(1)建立一個名為Identifier 的屬性,其值為為Result(3),唯讀,不可刪除
5. 從範圍鏈中移除Result(1)
6. 返回Result(3)

官方文檔讀起來十分拗口。簡單來說,ECMAScript是通過上下文來區分這兩者的:假如 function foo(){} 是一個賦值運算式的一部分,則認為它是一個函數運算式。而如果 function foo(){} 被包含在一個函數體內,或者位於程式(的最上層)中,則將它作為一個函式宣告來解析。顯然,在省略標識符的情況下,“運算式” 也就只能是運算式了。

複製代碼 代碼如下:

function foo(){}; // 聲明,因為它是程式的一部分
 
var bar = function foo(){}; // 運算式,因為它是賦值表達(AssignmentExpression)的一部分
 
new function bar(){}; // 運算式,因為它是New運算式(NewExpression)的一部分
 
(function(){
    function bar(){}; // 聲明,因為它是函數體(FunctionBody)的一部分
})();

還有一種情況:

複製代碼 代碼如下:

(function foo(){})

這種情況也是函數運算式,它被包含在一對圓括弧中的函數,在其上下文環境中,()構成了一個分組操作符,而分組操作符只能包含運算式,更多的例子:

複製代碼 代碼如下:

function foo(){}; // 函式宣告
 
(function foo(){}); // 函數運算式:注意它被包含在分組操作符中
 
try {
(var x = 5); // 分組操作符只能包含運算式,不能包含語句(這裡的var就是語句)
}
catch(err) {
// SyntaxError(因為“var x = 5”是一個語句,而不是運算式——對錶達式求值必須傳回值,但對語句求值則未必傳回值。——譯
}

下面簡單說說函式宣告與函數運算式的異同。聲明和運算式的行為存在著十分微妙而又十分重要的差別。

  首先,函式宣告會在任何錶達式被解析和求值之前先行被解析和求值。即使聲明位於原始碼中的最後一行,它也會先於同一範圍中位於最前面的運算式被求值。還是看個例子更容易理解。在下面這個例子中,函數 fn 是在 alert 後面聲明的。但是,在alert 執行的時候,fn已經有定義了:

複製代碼 代碼如下:

alert(fn()); //輸出Helloworld!  
 
function fn() {
return 'Helloworld!';
}


簡單總結,區別在什麼地方呢?

1. 聲明總是在範圍開始時先行解析;
2. 運算式在遇到時候才運算。

函式宣告還有另外一個重要的特點,即通過條件陳述式控制函式宣告的行為並未標準化,因此不同環境下可能會得到不同的結果。即是:

複製代碼 代碼如下:

// 千萬不要這樣做!
// 不同瀏覽器會有不同返回結果,
 
if (true) {
function foo() {
return 'first';
}
}
else {
function foo() {
return 'second';
}
}
foo();
 
 
// 記住,這種情況下要使用函數運算式:
var foo;
if (true) {
foo = function() {
return 'first';
};
}
else {
foo = function() {
return 'second';
};
}
foo();

 那麼,使用函式宣告的實際規則到底是什嗎? 

  FunctionDeclaration(函式宣告)只能出現在Program(程式)或FunctionBody(函數體)內。從句法上講,它們 不能出現在Block(塊)({ ... })中,例如不能出現在 if、while 或 for 語句中。因為 Block(塊) 中只能包含Statement(語句), 而不能包含FunctionDeclaration(函式宣告)這樣的SourceElement(源元素)。

  另一方面,仔細看一看產生規則也會發現,唯一可能讓Expression(運算式)出現在Block(塊)中情形,就是讓它作為ExpressionStatement(運算式語句)的一部分。但是,規範明確規定了ExpressionStatement(運算式語句)不能以關鍵字function開頭。而這實際上就是說,FunctionExpression(函數運算式)同樣也不能出現在Statement(語句)或Block(塊)中(別忘了Block(塊)就是由Statement(語句)構成的)。

  由於存在上述限制,只要函數出現在塊中(像上面例子中那樣),實際上就應該將其看作一個語法錯誤,而不是什麼函式宣告或運算式。

  

  那麼我們應該在什麼時候使用函式宣告或函數運算式呢?函式宣告只能出現在“程式碼”中,意味著只能在其它函數體中或者全域空間;它們的定義不能不能賦值給一個變數或屬性,或者作為一個參數傳遞出現在函數調用中;下面的例子是函式宣告的允許的用法,foo(),bar()和local()都是通過函式宣告模式聲明:

複製代碼 代碼如下:

// 全域環境
function foo() {}
 
function local() {
// 局部環境
    function bar() {}
        return bar;
}

  當你在文法上不能使用函式宣告的時候,你就可以使用函數運算式。比如:傳遞一個函數作為參數或者在對象字面量中定義一個函數:


複製代碼 代碼如下:

// 這是一個匿名函數運算式
callMe(function () {
 
//傳遞一個函數作為參數
});
 
// 這是一個具名函數運算式
callMe(function me() {
 
// 傳遞一個函數作為參數,函數名為me
});
 
// 其他函數運算式
var myobject = {
    say: function () {
 
// I am a function expression
}
};

學識有限,如有錯誤,歡迎指正。

聯繫我們

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

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

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.