在上一篇文章中我們通過(guò)變量在堆棧中的存儲(chǔ)和訪問(wèn)知道了腳本語(yǔ)言的執(zhí)行規(guī)則是 先定義嬉荆,后執(zhí)行。那么我們就來(lái)驗(yàn)證一下在我們的定義代碼中除了var一個(gè)對(duì)象之外蒋失,聲明函數(shù)和執(zhí)行函數(shù)的時(shí)候是否遵從這個(gè)執(zhí)行規(guī)則。
我們來(lái)看一點(diǎn)簡(jiǎn)單的代碼:
demo.png
輸出的三次a分別是什么?我們可以用先定義后執(zhí)行的思想分析一下
上面的代碼塊中 定義代碼有:
1. var a //定義變量a
2. function fn(){} //聲明一個(gè)叫fn的函數(shù)
那么問(wèn)題來(lái)了移剪,函數(shù) fn 內(nèi)部的也有定義代碼 var a
,會(huì)不會(huì)先執(zhí)行呢薪者?是這樣的:因?yàn)橹挥挟?dāng)我們調(diào)用函數(shù)的時(shí)候才會(huì)執(zhí)行函數(shù)內(nèi)部的定義代碼纵苛,所以不會(huì)執(zhí)行函數(shù)內(nèi)部的var a
,目前只有兩個(gè)定義代碼并且從上往下執(zhí)行。
當(dāng)我們沒有定義代碼之后攻人,我們就會(huì)執(zhí)行 執(zhí)行代碼取试,也是從上往下執(zhí)行。
執(zhí)行代碼第一步就是fn();
怀吻,那么當(dāng)調(diào)用這個(gè)函數(shù)的時(shí)候就會(huì)進(jìn)入到函數(shù)內(nèi)部里面去瞬浓,我們首先還是會(huì)按照先定義后執(zhí)行的方式
函數(shù)內(nèi)部:
1.var a //定義函數(shù)內(nèi)部的a
2.console.log(a) //輸出a 我們發(fā)現(xiàn)只定義沒有賦值所以是undefined
3. a=1
當(dāng)函數(shù)內(nèi)部執(zhí)行完畢之后,將跳出函數(shù)繼續(xù)往下面執(zhí)行console.log(a)
默認(rèn)輸出全局環(huán)境下的a
所以整體的執(zhí)行順序如下圖:
最后會(huì)依次輸出 undefined object object
我們只要記住這個(gè)執(zhí)行順序來(lái)冷靜的分析烙博,基本上可以減少很多意想不到的錯(cuò)誤瑟蜈。
函數(shù)表達(dá)式
只有function fn()
這樣的函數(shù)聲明 才是定義代碼
而自執(zhí)行函數(shù)(function(){})()
和var fn1 = function(){}
他們都屬于 函數(shù)表達(dá)式不屬于定義代碼。
我們可以通過(guò)代碼來(lái)看函數(shù)表達(dá)式與函數(shù)聲明的區(qū)別
為什么會(huì)報(bào)錯(cuò)呢铺根?按照我們的理解,來(lái)分析一下執(zhí)行順序:
正因?yàn)樗呛瘮?shù)表達(dá)式乔宿,而不是函數(shù)聲明位迂,所以屬于執(zhí)行代碼,按照從上往下的執(zhí)行順序详瑞,在調(diào)用
fn1
之后執(zhí)行掂林,當(dāng)調(diào)用fn1
時(shí)它還沒有被賦值為一個(gè)函數(shù),所以就會(huì)報(bào)錯(cuò)了坝橡。
在分析原因之前我們先了解一下內(nèi)存中是如何處理的
當(dāng)程序代碼解析時(shí) 看到通過(guò)函數(shù)聲明的方式創(chuàng)建函數(shù)
fn(){}
的時(shí)候泻帮, 這個(gè)函數(shù) fn 是創(chuàng)建在于堆中的,這個(gè)fn在內(nèi)存中叫做函數(shù)的“引用名稱”计寇,同時(shí)會(huì)有一個(gè)內(nèi)存地址锣杂,當(dāng)在運(yùn)行時(shí)環(huán)境試圖調(diào)用fn()時(shí),會(huì)先在棧中檢索看看有沒有定義fn這個(gè)變量番宁,如果棧中沒有元莫,就會(huì)去堆中找fn的引用名稱。
注意:匿名函數(shù)是沒有引用名稱的蝶押,它只有內(nèi)存地址踱蠢。所以按照上面的說(shuō)法當(dāng)我們 var fn1 時(shí) 會(huì)在棧中開辟一塊空間叫 fn1,定義代碼結(jié)束之后會(huì)執(zhí)行執(zhí)行代碼棋电,會(huì)在“運(yùn)行時(shí)環(huán)境”中調(diào)用fn1()這個(gè)函數(shù)茎截,然后我們會(huì)現(xiàn)在棧中檢索發(fā)現(xiàn)我們定義了fn1這個(gè)變量,但是這個(gè)變量里面沒有存儲(chǔ)任何的內(nèi)存地址赶盔,fn1并不是一個(gè)函數(shù)稼虎,所以就會(huì)報(bào)錯(cuò)。
如果我們把代碼換一個(gè)順序:最后再調(diào)用fn1()
那么理所當(dāng)然的招刨,會(huì)把匿名函數(shù)的內(nèi)存地址給fn1 ,這樣就會(huì)輸出1了。