欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS中作用域和變量提升(hoisting)的深入理解

 更新時(shí)間:2016年10月31日 09:35:14   投稿:daisy  
相信大家也都發(fā)現(xiàn)了,在網(wǎng)上關(guān)于JS的變量和作用域的文章有很多,但真正能講清楚,能深入理解的文章很少。在閱讀了很多人的文章以后,我決定綜合起來(lái),結(jié)合實(shí)際代碼,希望能夠以一個(gè)比較清楚完整的方式讓大家真正理解。有需要的朋友們下面來(lái)一起看看吧。

作用域(Scoping)

對(duì)于Javascript初學(xué)者來(lái)說(shuō),一個(gè)最迷惑的地方就是作用域;事實(shí)上,不光是初學(xué)者。我就見(jiàn)過(guò)一些有經(jīng)驗(yàn)的javascript程序員,但他們對(duì)scope理解不深。javascript作用域之所以迷惑,是因?yàn)樗绦蛘Z(yǔ)法本身長(zhǎng)的像C家族的語(yǔ)言。我對(duì)作用域的理解是只會(huì)對(duì)某個(gè)范圍產(chǎn)生作用,而不會(huì)對(duì)外產(chǎn)生影響的封閉空間。在這樣的一些空間里,外部不能訪問(wèn)內(nèi)部變量,但內(nèi)部可以訪問(wèn)外部變量。

c語(yǔ)言的變量分為全局變量和局部變量,全局變量的作用范圍是任何文件和函數(shù)訪問(wèn)(當(dāng)然,對(duì)于非變量定義的其他c文件,需要使用extern關(guān)鍵字進(jìn)行申明,使用static關(guān)鍵字也可以將作用范圍限定在當(dāng)前文件中),局部變量的作用范圍就是從申明到最近的大括號(hào)涵蓋的塊級(jí)范圍。java則無(wú)全局變量,有類變量,成員變量和局部變量,作用范圍根據(jù)public,protected,private等訪問(wèn)權(quán)限有不同的作用范圍,這里就不多述。

JS作用域有哪些?

在ES5中,js只有兩種形式的作用域:全局作用域和函數(shù)作用域。

全局作用域其實(shí)是全局對(duì)象的作用域,任意地方都可以訪問(wèn)到(如果沒(méi)有被函數(shù)作用域覆蓋)。

函數(shù)對(duì)象作用域跟c的局部變量作用域是不同的,它的作用域是整個(gè)函數(shù)范圍,不論他是在函數(shù)的任意位置申明的!這就是所謂的hoisting,也就是變量提升的概念。不過(guò)不著急,下面會(huì)專門(mén)針對(duì)hoisting來(lái)進(jìn)行解釋。

不過(guò),在ES6中,新增了一個(gè)塊級(jí)作用域(最近的大括號(hào)涵蓋的范圍),但是僅限于let方式申明的變量。
作用域演示:

定義變量時(shí),如果不寫(xiě)var,比如 i=0,則會(huì)被定義為全局變量,作用域?yàn)槿肿饔糜?,否則為局部變量,作用域?yàn)楹瘮?shù)作用域。上面第一行的var i=0,之所以說(shuō)它是全局變量,是因?yàn)樗呀?jīng)是在全局區(qū)申明的了,并不在函數(shù)范圍內(nèi),因此跟 i=0 是一樣的。

至于,為什么結(jié)果會(huì)是這樣,繼續(xù)往下看就知道了。

申明形式

變量聲明:

函數(shù)申明:

變量提升(Hoisting)

引出一個(gè)問(wèn)題

下面這段代碼會(huì)輸出什么內(nèi)容?

這道題我面試過(guò)很多人,大多數(shù)人都說(shuō)輸出的是日期。但真實(shí)的結(jié)果是undefined。為什么是這樣呢?這里就引出了一個(gè)概念--hoisting,中文的意思就是變量提升。MDN中對(duì)變量hoisting的解釋是這樣的:

var hoisting

Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top. This also means that a variable can appear to be used before it's declared. This behavior is called "hoisting", as it appears that the variable declaration is moved to the top of the function or global code.

這段話翻譯下來(lái)就是

因?yàn)樽兞可昝魇窃谌我獯a執(zhí)行前處理的,在代碼區(qū)中任意地方申明變量和在最開(kāi)始(最上面)的地方申明是一樣的。也就是說(shuō),看起來(lái)一個(gè)變量可以在申明之前被使用!這種行為就是所謂的“hoisting”,也就是變量提升,看起來(lái)就像變量的申明被自動(dòng)移動(dòng)到了函數(shù)或全局代碼的最頂上。

注意:僅僅是申明提升了,定義并不會(huì)被提升。

如此,上面這段代碼其實(shí)就是下面的形式:

所以,這樣就應(yīng)該理解了,console輸出的時(shí)候,tmp變量?jī)H僅是申明了但未定義,所以輸出應(yīng)該是undefined。

這里需要說(shuō)明的是,雖然所有的申明(包括ES5的var、function,和ES6的function *、let、const、class)都會(huì)被提升,但是var、function、function *和let、const、class的的提升卻并不相同!具體原因可以看這里的說(shuō)明(大體的意思是雖然let,const,class也被提升了,但是卻并不會(huì)被初始化,這時(shí)候去訪問(wèn)他們則會(huì)報(bào)ReferenceError異常,他們需要到語(yǔ)句執(zhí)行的時(shí)候才會(huì)被初始化,而在被初始化之前的狀態(tài)叫做temporal dead zone)。我們來(lái)看一段代碼就知道了:


這里a被提升,但因?yàn)槎x在后,所以輸出undefined

這里a雖然被提升,但卻報(bào)了引用錯(cuò)誤!

之所以或這樣

因?yàn)檫@樣的原因,推薦的做法是在申明變量的時(shí)候,將所用的變量都寫(xiě)在作用域(全局作用域或函數(shù)作用域)的最頂上,這樣代碼看起來(lái)就會(huì)更清晰,更容易看出來(lái)那個(gè)變量是來(lái)自函數(shù)作用域的,哪個(gè)又是來(lái)自作用域鏈(本文不對(duì)此多做解釋,請(qǐng)讀者自行百度,有機(jī)會(huì)再補(bǔ)充說(shuō)明)。

重復(fù)聲明

上面的輸出其實(shí)是:1 2 2。雖然看起來(lái)里面x申明了兩次,但上面說(shuō)了,js的var變量只有全局作用域和函數(shù)作用域兩種,且申明會(huì)被提升,因此實(shí)際上x(chóng)只會(huì)在最頂上開(kāi)始的地方申明一次,var x=2的申明會(huì)被忽略,僅用于賦值。也就是說(shuō)上面的代碼實(shí)際上跟下面是一致的。

函數(shù)和變量同時(shí)提升的問(wèn)題

如果是函數(shù)和變量類型同時(shí)申明定義了,會(huì)發(fā)生什么事情呢?看下面的代碼


A

上面的輸出結(jié)果其實(shí)是: function foo(){} ,也就是函數(shù)內(nèi)容。

而如果是這樣的形式呢


B

它的輸出卻變成:undefined

為什么會(huì)這樣呢?

原來(lái)函數(shù)提升分為兩種情況:

      一種:函數(shù)申明。就是上面A,function foo(){}這種形式

     另一種:函數(shù)表達(dá)式。就是上面B,var foo=function(){}這種形式

第二種形式其實(shí)就是var變量的聲明定義,因此上面的B輸出結(jié)果為undefined應(yīng)該就能理解了。

而第一種函數(shù)申明的形式,在提升的時(shí)候,會(huì)被整個(gè)提升上去,包括函數(shù)定義的部分!因此A跟下面的這種方式是等價(jià)的!

原因是因?yàn)椋?、函數(shù)聲明被提升到最頂上;2、申明只進(jìn)行一次,因此后面var foo='i am text'的申明會(huì)被忽略。

并且函數(shù)申明的優(yōu)先級(jí)優(yōu)于變量申明,所以以下形式的輸出,同樣是函數(shù)內(nèi)容:

總結(jié)

要徹底理解JS的作用域和Hoisting,只要記住以下三點(diǎn)即可:

      1、所有申明都會(huì)被提升到作用域的最頂上

      2、同一個(gè)變量申明只進(jìn)行一次,并且因此其他申明都會(huì)被忽略

      3、函數(shù)聲明的優(yōu)先級(jí)優(yōu)于變量申明,且函數(shù)聲明會(huì)連帶定義一起被提升

注意:

通過(guò)with語(yǔ)句,可以臨時(shí)改變運(yùn)行期上下文的作用域鏈,此時(shí)的對(duì)非var定義的變量進(jìn)行訪問(wèn),會(huì)首先訪問(wèn)with中對(duì)象的屬性,然后才會(huì)向上順著作用域鏈向上檢查該屬性。

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能有所幫助,如果有疑問(wèn)大家可以留言交流。

相關(guān)文章

最新評(píng)論