詳解Javascript 函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別

Javascript Function無(wú)處不在崎淳,而且功能強(qiáng)大!通過(guò)Javascript函數(shù)可以讓JS具有面向?qū)ο蟮囊恍┨卣髑汕冢瑢?shí)現(xiàn)封裝、繼承等弄匕,也可以讓代碼得到復(fù)用颅悉。但事物都有兩面性,Javascript函數(shù)有的時(shí)候也比較“任性”迁匠,你如果不了解它的“性情”签舞,它很可能給你制造出一些意想不到的麻煩(bugs)出來(lái)秕脓。

Javascript Function有兩種類(lèi)型:

1)函數(shù)聲明(Function Declaration);

 // 函數(shù)聲明
    function funDeclaration(type){ return type==="Declaration";
    }

2)函數(shù)表達(dá)式(Function Expression)。

// 函數(shù)表達(dá)式
    var funExpression = function(type){ return type==="Expression";
    }

上面的代碼看起來(lái)很類(lèi)似儒搭,感覺(jué)也沒(méi)什么太大差別。但實(shí)際上芙贫,Javascript函數(shù)上的一個(gè)“陷阱”就體現(xiàn)在Javascript兩種類(lèi)型的函數(shù)定義上搂鲫。下面看兩段代碼(分別標(biāo)記為代碼1段和代碼2段):

 funDeclaration("Declaration");//=> true
function funDeclaration(type){ 
return type==="Declaration";}
funExpression("Expression");//=>error
var funExpression = function(type){ 
return type==="Expression"; 
}

用函數(shù)聲明創(chuàng)建的函數(shù)funDeclaration可以在funDeclaration定義之前就進(jìn)行調(diào)用;而用函數(shù)表達(dá)式創(chuàng)建的funExpression函數(shù)不能在funExpression被賦值之前進(jìn)行調(diào)用磺平。
為什么會(huì)這樣呢魂仍?!這就要理解Javascript Function兩種類(lèi)型的區(qū)別:用函數(shù)聲明創(chuàng)建的函數(shù)可以在函數(shù)解析后調(diào)用(解析時(shí)進(jìn)行等邏輯處理)拣挪;而用函數(shù)表達(dá)式創(chuàng)建的函數(shù)是在運(yùn)行時(shí)進(jìn)行賦值擦酌,且要等到表達(dá)式賦值完成后才能調(diào)用。
這個(gè)區(qū)別看似微小菠劝,但在某些情況下確實(shí)是一個(gè)難以發(fā)現(xiàn)的陷阱赊舶。出現(xiàn)這個(gè)陷阱的本質(zhì)原因體現(xiàn)在這兩種類(lèi)型在Javascript function hoisting(函數(shù)提升)和運(yùn)行時(shí)機(jī)(解析時(shí)/運(yùn)行時(shí))上的差異。關(guān)于變量提升赶诊,可參見(jiàn)我的另外一篇博文http://www.cnblogs.com/isaboy/p/javascript_hoisting.html笼平。上面兩段代碼的函數(shù)提升可示意為下圖:

image

代碼1段JS函數(shù)等同于: </pre>

function funDeclaration(type){ return type==="Declaration";}
funDeclaration("Declaration");//=> true

代碼2段JS函數(shù)等同于

var funExpression;
    funExpression("Expression");//==>error
    funExpression = function(type){ return type==="Expression";
    }

上述代碼在運(yùn)行時(shí),只定義了funExpression變量舔痪,但值為undefined寓调。因此不能在undefined上進(jìn)行函數(shù)調(diào)用。此時(shí)funExpression賦值語(yǔ)句還沒(méi)執(zhí)行到锄码。為了進(jìn)一步加深JS函數(shù)兩種類(lèi)型的區(qū)別夺英,下面給出一個(gè)更具迷惑性的示例,請(qǐng)看下面的代碼(代碼段4):

var sayHello;
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        function sayHey() {
            console.log("sayHey");
        }
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        function sayHey() {
            console.log("sayHey2");
        }
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

分析:sayHey是用函數(shù)聲明創(chuàng)建的滋捶,在JS解析時(shí)JS編譯器將函數(shù)定義進(jìn)行了函數(shù)提升痛悯,也就是說(shuō),在解析JS代碼的時(shí)候炬太,JS編譯器(條件判斷不形成新的作用域灸蟆,兩個(gè)sayHey函數(shù)定義都被提升到條件判斷之外)檢測(cè)到作用域內(nèi)有兩個(gè)同名的sayHey定義,第一個(gè)定義先被提升亲族,第二個(gè)定義接著被提升(第二個(gè)定義在第一個(gè)定義之下)炒考,第二個(gè)定義覆蓋了第一個(gè)sayHey定義,所以sayHey()輸出sayHey2霎迫;而sayHello是用函數(shù)表達(dá)式創(chuàng)建的斋枢,其表達(dá)式的內(nèi)容是在JS運(yùn)行時(shí)(不是解析時(shí))才能確定(這里條件判斷就起到作用了),所以sayHello表達(dá)式執(zhí)行了第一個(gè)函數(shù)定義并賦值知给,則sayHello()輸出sayHello

image

代碼段4的代碼實(shí)際上等同于下面的代碼(代碼段5):

var sayHello;
    function sayHey() {
            console.log("sayHey");
        }
    function sayHey() {
            console.log("sayHey2");
    }
    console.log(typeof (sayHey));//=>function    
    console.log(typeof (sayHo));//=>undefined
    if (true) {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello");
    }
    } else {
        //hoisting...
        sayHello = function sayHo() {
            console.log("sayHello2");
        }
    }    
    sayHey();// => sayHey2    
    sayHello();// => sayHello

有的人也許會(huì)懷疑函數(shù)sayHey的定義是第二個(gè)覆蓋第一個(gè)了么瓤帚?我們可以把sayHey的源代碼進(jìn)行輸出描姚,有圖有真相,如下圖所示:

image

  總結(jié)

Javascript 中函數(shù)聲明和函數(shù)表達(dá)式是存在區(qū)別的戈次,函數(shù)聲明在JS解析時(shí)進(jìn)行函數(shù)提升轩勘,因此在同一個(gè)作用域內(nèi),不管函數(shù)聲明在哪里定義怯邪,該函數(shù)都可以進(jìn)行調(diào)用绊寻。而函數(shù)表達(dá)式的值是在JS運(yùn)行時(shí)確定,并且在表達(dá)式賦值完成后悬秉,該函數(shù)才能調(diào)用澄步。這個(gè)微小的區(qū)別,可能會(huì)導(dǎo)致JS代碼出現(xiàn)意想不到的bug,讓你陷入莫名的陷阱中和泌。

最后附上代碼段4中sayHello和sayHey兩個(gè)函數(shù)的核心步驟(個(gè)人理解村缸,若有異議歡迎留言探討):

image

上圖為sayHello函數(shù)執(zhí)行的主要步驟示意圖。

image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末武氓,一起剝皮案震驚了整個(gè)濱河市梯皿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌聋丝,老刑警劉巖索烹,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異弱睦,居然都是意外死亡百姓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)况木,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)垒拢,“玉大人,你說(shuō)我怎么就攤上這事火惊∏罄啵” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵屹耐,是天一觀的道長(zhǎng)尸疆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)惶岭,這世上最難降的妖魔是什么寿弱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮按灶,結(jié)果婚禮上症革,老公的妹妹穿的比我還像新娘。我一直安慰自己鸯旁,他們只是感情好噪矛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布量蕊。 她就那樣靜靜地躺著,像睡著了一般艇挨。 火紅的嫁衣襯著肌膚如雪残炮。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天缩滨,我揣著相機(jī)與錄音吉殃,去河邊找鬼。 笑死楷怒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瓦灶。 我是一名探鬼主播鸠删,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贼陶!你這毒婦竟也來(lái)了刃泡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤碉怔,失蹤者是張志新(化名)和其女友劉穎烘贴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體撮胧,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡桨踪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芹啥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锻离。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖墓怀,靈堂內(nèi)的尸體忽然破棺而出汽纠,到底是詐尸還是另有隱情,我是刑警寧澤傀履,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布虱朵,位于F島的核電站,受9級(jí)特大地震影響钓账,放射性物質(zhì)發(fā)生泄漏碴犬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一官扣、第九天 我趴在偏房一處隱蔽的房頂上張望翅敌。 院中可真熱鬧,春花似錦惕蹄、人聲如沸蚯涮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遭顶。三九已至张峰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棒旗,已是汗流浹背喘批。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铣揉,地道東北人饶深。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像逛拱,于是被迫代替她去往敵國(guó)和親敌厘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容