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