JavaScript中的函數聲明和函數表達式區(qū)別淺析
記得在面試騰訊實習生的時候,面試官問了我這樣一道問題。
//下述兩種聲明方式有什么不同
function foo(){};
var bar = function foo(){};
當初只知道兩種聲明方式一個是函數聲明一個是函數表達式,具體有什么不同沒能說得很好。最近正好看到這方面的書籍,就想好好總結一番。
在ECMAScript中,有兩個最常用的創(chuàng)建函數對象的方法,即使用函數表達式或者使用函數聲明。對此,ECMAScript規(guī)范明確了一點,即是,即函數聲明 必須始終帶有一個標識符(Identifier),也就是我們所說的函數名,而函數表達式則可以省略。
函數聲明:
function Identifier ( FormalParameterList opt){ FunctionBody }
函數聲明解析過程如下:
1. 創(chuàng)建一個new Function對象,F(xiàn)ormalParameterList指定參數,F(xiàn)unctionBody指定函數體。將當前正在運行環(huán)境中作用域鏈作為它的作用域。
2. 為當前變量對象創(chuàng)建一個名為Identifier的屬性,值為Result(1)。
函數表達式:
(函數表達式分為匿名和具名函數表達式)
function Identifier opt( FormalParameterList opt){ FunctionBody } //這里是具名函數表達式
具名函數表達式的解析過程如下:
1. 創(chuàng)建一個new Object對象
2. 將Result(1)添加到作用域鏈的頂端
3. 創(chuàng)建一個new Function對象,F(xiàn)ormalParameterList指定參數,F(xiàn)unctionBody指定函數體。將當前正在運行的執(zhí)行環(huán)境中作用域鏈作為它的作用域。
4. 為Result(1)創(chuàng)建一個名為Identifier 的屬性,其值為為Result(3),只讀,不可刪除
5. 從作用域鏈中移除Result(1)
6. 返回Result(3)
官方文檔讀起來十分拗口。簡單來說,ECMAScript是通過上下文來區(qū)分這兩者的:假如 function foo(){} 是一個賦值表達式的一部分,則認為它是一個函數表達式。而如果 function foo(){} 被包含在一個函數體內,或者位于程序(的最上層)中,則將它作為一個函數聲明來解析。顯然,在省略標識符的情況下,“表達式” 也就只能是表達式了。
function foo(){}; // 聲明,因為它是程序的一部分
var bar = function foo(){}; // 表達式,因為它是賦值表達(AssignmentExpression)的一部分
new function bar(){}; // 表達式,因為它是New表達式(NewExpression)的一部分
(function(){
function bar(){}; // 聲明,因為它是函數體(FunctionBody)的一部分
})();
還有一種情況:
(function foo(){})
這種情況也是函數表達式,它被包含在一對圓括號中的函數,在其上下文環(huán)境中,()構成了一個分組操作符,而分組操作符只能包含表達式,更多的例子:
function foo(){}; // 函數聲明
(function foo(){}); // 函數表達式:注意它被包含在分組操作符中
try {
(var x = 5); // 分組操作符只能包含表達式,不能包含語句(這里的var就是語句)
}
catch(err) {
// SyntaxError(因為“var x = 5”是一個語句,而不是表達式——對表達式求值必須返回值,但對語句求值則未必返回值?!g
}
下面簡單說說函數聲明與函數表達式的異同。聲明和表達式的行為存在著十分微妙而又十分重要的差別。
首先,函數聲明會在任何表達式被解析和求值之前先行被解析和求值。即使聲明位于源代碼中的最后一行,它也會先于同一作用域中位于最前面的表達式被求值。還是看個例子更容易理解。在下面這個例子中,函數 fn 是在 alert 后面聲明的。但是,在alert 執(zhí)行的時候,fn已經有定義了:
alert(fn()); //輸出Helloworld!
function fn() {
return 'Helloworld!';
}
簡單總結,區(qū)別在什么地方呢?
1. 聲明總是在作用域開始時先行解析;
2. 表達式在遇到時候才運算。
函數聲明還有另外一個重要的特點,即通過條件語句控制函數聲明的行為并未標準化,因此不同環(huán)境下可能會得到不同的結果。即是:
// 千萬不要這樣做!
// 不同瀏覽器會有不同返回結果,
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();
那么,使用函數聲明的實際規(guī)則到底是什么?
FunctionDeclaration(函數聲明)只能出現(xiàn)在Program(程序)或FunctionBody(函數體)內。從句法上講,它們 不能出現(xiàn)在Block(塊)({ ... })中,例如不能出現(xiàn)在 if、while 或 for 語句中。因為 Block(塊) 中只能包含Statement(語句), 而不能包含F(xiàn)unctionDeclaration(函數聲明)這樣的SourceElement(源元素)。
另一方面,仔細看一看產生規(guī)則也會發(fā)現(xiàn),唯一可能讓Expression(表達式)出現(xiàn)在Block(塊)中情形,就是讓它作為ExpressionStatement(表達式語句)的一部分。但是,規(guī)范明確規(guī)定了ExpressionStatement(表達式語句)不能以關鍵字function開頭。而這實際上就是說,F(xiàn)unctionExpression(函數表達式)同樣也不能出現(xiàn)在Statement(語句)或Block(塊)中(別忘了Block(塊)就是由Statement(語句)構成的)。
由于存在上述限制,只要函數出現(xiàn)在塊中(像上面例子中那樣),實際上就應該將其看作一個語法錯誤,而不是什么函數聲明或表達式。
那么我們應該在什么時候使用函數聲明或函數表達式呢?函數聲明只能出現(xiàn)在“程序代碼”中,意味著只能在其它函數體中或者全局空間;它們的定義不能不能賦值給一個變量或屬性,或者作為一個參數傳遞出現(xiàn)在函數調用中;下面的例子是函數聲明的允許的用法,foo(),bar()和local()都是通過函數聲明模式聲明:
// 全局環(huán)境
function foo() {}
function local() {
// 局部環(huán)境
function bar() {}
return bar;
}
當你在語法上不能使用函數聲明的時候,你就可以使用函數表達式。比如:傳遞一個函數作為參數或者在對象字面量中定義一個函數:
// 這是一個匿名函數表達式
callMe(function () {
//傳遞一個函數作為參數
});
// 這是一個具名函數表達式
callMe(function me() {
// 傳遞一個函數作為參數,函數名為me
});
// 其他函數表達式
var myobject = {
say: function () {
// I am a function expression
}
};
學識有限,如有錯誤,歡迎指正。
- JavaScript中函數聲明與函數表達式的區(qū)別詳解
- 淺談javascript 函數表達式和函數聲明的區(qū)別
- JavaScript中函數表達式和函數聲明及函數聲明與函數表達式的不同
- 詳解JavaScript中的函數聲明和函數表達式
- js中函數聲明與函數表達式
- 淺析javascript中函數聲明和函數表達式的區(qū)別
- javascript函數聲明和函數表達式區(qū)別分析
- Javascript學習筆記之 函數篇(一) : 函數聲明和函數表達式
- javascript 函數聲明與函數表達式的區(qū)別介紹
- Javascript中的函數聲明與函數表達式(奇技淫巧)
- 理解 javascript 中的函數表達式與函數聲明
相關文章
JavaScript實現(xiàn)三級聯(lián)動菜單實例代碼
這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)三級聯(lián)動菜單實例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06uni-app使用uni-download和uni.saveFile下載保存文件遇到的問題及解決方法
這篇文章主要給大家介紹了關于uni-app使用uni-download和uni.saveFile下載保存文件遇到的問題及解決方法的相關資料,文中給出了詳細的代碼示例,需要的朋友可以參考下2024-01-01JavaScript常用正則驗證函數實例小結【年齡,數字,Email,手機,URL,日期等】
這篇文章主要介紹了JavaScript常用正則驗證函數,結合實例形式總結分析了javascript針對年齡、數字、Email、手機、URL、日期等格式常用正則驗證技巧,需要的朋友可以參考下2017-01-01