《你不知道的JavaScript》-作用域是什么(01)

一禁舷、編譯器、引擎、作用域

眾所周知罢吃,JavaScript是一門編譯語言矾柜,簡(jiǎn)單的說,任何JavaScript代碼片段在執(zhí)行前都要進(jìn)行編譯

以片段var a = 2做一個(gè)示例泊业、JavaScript編譯器首先會(huì)對(duì)var a = 2這段程序進(jìn)行編譯把沼,然后做好執(zhí)行它的準(zhǔn)備,并且通常馬上就會(huì)執(zhí)行它吁伺。首先需要先了解JavaScript編譯器中的幾個(gè)重要角色饮睬。

引擎:從頭到尾負(fù)責(zé)整個(gè)JavaScript程序的編譯及執(zhí)行過程。

編譯器:引擎的好朋友之一篮奄,負(fù)責(zé)語法分析及代碼生成等臟活累活

作用域:引擎的另一個(gè)好朋友捆愁,負(fù)責(zé)收集并維護(hù)由所有聲明的標(biāo)識(shí)符(變量)組成的一系列查詢,并實(shí)施一套非常嚴(yán)格的規(guī)則窟却,確定當(dāng)前執(zhí)行的代碼對(duì)這些標(biāo)識(shí)符的訪問權(quán)限昼丑。

1、當(dāng)你看到 var = 2這段程序時(shí)夸赫,編譯器會(huì)詢問作用域是否存在一個(gè)該名稱的變量存在于同一個(gè)作用域的集合中菩帝,如果是,編譯器會(huì)忽略該聲明,繼續(xù)進(jìn)行編譯呼奢;否則他會(huì)要求作用域在當(dāng)前作用域的集合中聲明一個(gè)新的變量宜雀,并命名為a

2、加下來編譯器會(huì)為引擎生成運(yùn)行時(shí)所需的代碼握础,這些代碼被用來處理a=2這個(gè)賦值的操作辐董,引擎運(yùn)行時(shí)會(huì)首先詢問作用域,在當(dāng)前的作用域集合中是否存在一個(gè)叫a的變量禀综,如果是简烘,引擎就會(huì)使用這個(gè)變量,如果否定枷,引擎會(huì)繼續(xù)查找該變量(查找規(guī)則后面文章會(huì)具體描述)

如果引擎最終找到了a變量孤澎,就會(huì)將2賦值給它。否則引擎就會(huì)拋出一個(gè)異常依鸥。

總結(jié):變量的賦值操作會(huì)執(zhí)行兩個(gè)動(dòng)作亥至,首先編譯器會(huì)在當(dāng)前作用域中聲明一個(gè)變量(如果之前沒聲明過)
,然后再運(yùn)行時(shí)引擎會(huì)在作用域中查找該變量贱迟,如果能找到就會(huì)對(duì)它賦值姐扮。

引擎查找變量的規(guī)則,分為兩種衣吠,LHS 查詢和RHS 查詢茶敏。查找規(guī)則不同,會(huì)影響最終的查找結(jié)果缚俏。

LHR:當(dāng)變量出現(xiàn)在賦值操作的左側(cè)時(shí)進(jìn)行LHS查詢惊搏,

// LHR
// 找到變量的容器,從而對(duì)其進(jìn)行賦值
a = 2

RHS: 非左側(cè)時(shí)忧换,即RHS恬惯,意為‘得到xx的值’, 進(jìn)行RHS查詢

// RHS
// 沒有對(duì)a進(jìn)行賦值,需要查找并取得a的值亚茬,這樣才能將值傳遞給console.log()
console.log(a)

eg:

function foo(a){
  console.log(a) // 2
}
foo(2)

引擎:我說作用域酪耳,我需要為foo進(jìn)行RHS引用。你見過它嗎刹缝?
作用域:別說碗暗,我還真見過,編譯器那小子剛剛聲明了它梢夯。它是一個(gè)函數(shù)言疗,給你。
引擎:哥們太夠意思了颂砸!好吧噪奄,我來執(zhí)行一下foo死姚。
引擎:作用域,還有個(gè)事兒勤篮。我需要為a進(jìn)行LHS引用知允,這個(gè)你見過嗎?
作用域:這個(gè)也見過叙谨,編譯器最近把它聲名為foo的一個(gè)形式參數(shù)了,拿去吧保屯。
引擎:大恩不言謝手负,你總是這么棒。現(xiàn)在我要把2賦值給a姑尺。
引擎:哥們竟终,不好意思又來打擾你。我要為console進(jìn)行RHS引用切蟋,你見過它嗎统捶?
作用域:咱倆誰跟誰啊,再說我就是干這個(gè)柄粹。這個(gè)我也有喘鸟,console是個(gè)內(nèi)置對(duì)象。給你驻右。
引擎:么么噠什黑。我得看看這里面是不是有l(wèi)og(..)。太好了堪夭,找到了愕把,是一個(gè)函數(shù)。
引擎:哥們森爽,能幫我再找一下對(duì)a的RHS引用嗎恨豁?雖然我記得它,但想再確認(rèn)一次爬迟。
作用域:放心吧橘蜜,這個(gè)變量沒有變動(dòng)過,拿走雕旨,不謝扮匠。
引擎:真棒。我來把a(bǔ)的值凡涩,也就是2棒搜,傳遞進(jìn)log(..)。

二活箕、作用域嵌套

作用域是根據(jù)名稱查找變量的一套規(guī)則力麸。

當(dāng)一個(gè)塊或函數(shù)嵌套在另一個(gè)塊或函數(shù)中時(shí),就發(fā)生了作用域的嵌套。因此克蚂,在當(dāng)前作用域中無法找到某個(gè)變量時(shí)闺鲸,引擎就會(huì)在外層嵌套的作用域中繼續(xù)查找,直到找到該變量埃叭,或抵達(dá)最外層的作用域(也就是全局作用域)為止摸恍。

function foo(a){
  console.log(a+b)
}
var b = 2
foo(2) // 4

回顧一下前面的作用域和引擎之間的對(duì)話,上面代碼的對(duì)話如下赤屋。

引擎:foo的作用域兄弟立镶,你見過b嗎?我需要對(duì)它進(jìn)行RHS引用类早。
作用域:聽都沒聽過媚媒,走開。
引擎:foo的上級(jí)作用域兄弟涩僻,咦缭召?有眼不識(shí)泰山,原來你是全局作用域大哥逆日,太好了嵌巷。你見過b嗎?我需要對(duì)它進(jìn)行RHS引用室抽。
作用域:當(dāng)然了晴竞,給你吧。

遍歷嵌套作用域鏈的規(guī)則很簡(jiǎn)單:引擎從當(dāng)前的執(zhí)行作用域開始查找變量狠半,如果找不到噩死,就向上一級(jí)繼續(xù)查找。當(dāng)?shù)诌_(dá)最外層的全局作用域時(shí)神年,無論找到還是沒找到已维,查找過程都會(huì)停止。

可以把作用域鏈比喻成一個(gè)建筑

image.png

這個(gè)建筑代表程序中的嵌套作用域鏈已日。第一層樓代表當(dāng)前的執(zhí)行作用域垛耳,也就是你所處的位置。建筑的頂層代表全局作用域飘千。LHS和RHS引用都會(huì)在當(dāng)前樓層進(jìn)行查找堂鲜,如果沒有找到,就會(huì)坐電梯前往上一層樓护奈,如果還是沒有找到就繼續(xù)向上缔莲,以此類推。一旦抵達(dá)頂層(全局作用域)霉旗,可能找到了你所需的變量痴奏,也可能沒找到蛀骇,但無論如何查找過程都將停止。

三读拆、兩種查詢的區(qū)別擅憔?

在變量還沒有聲明的時(shí)候,這兩種查詢的行為是不一致的檐晕。

function foo(){
   a = 0
   console.log(b)
}
foo()

在上述代碼中暑诸,對(duì)于a是進(jìn)行的LHS查詢,對(duì)于b是進(jìn)行的RHS查詢辟灰。

如果LHS查詢?cè)谌肿饔糜蛑幸矡o法找到目標(biāo)變量屠列,全局作用域中就會(huì)熱心的創(chuàng)建一個(gè)具有該名稱的變量(非嚴(yán)格模式下會(huì)熱心創(chuàng)建,嚴(yán)格模式下禁止自動(dòng)或隱式的創(chuàng)建全局變量)伞矩。

如果RHS查詢?cè)谌肿饔糜蛑兴褜げ坏剿璧淖兞浚婢蜁?huì)拋出一個(gè)ReferenceError異常夏志。

如果你對(duì)RHS查詢到的變量進(jìn)行了不合理的操作乃坤,比如對(duì)一個(gè)非函數(shù)類型的值進(jìn)行函數(shù)調(diào)用等,那么引擎會(huì)拋出TypeError的異常沟蔑,

ReferenceError同作用域判別失敗相關(guān)湿诊,而TypeError則代表作用域判別成功了,但是對(duì)結(jié)果的操作是非法或不合理的瘦材。

老鐵厅须,覺得有用點(diǎn)贊。

參考資料:
《你不知道的JavaScript》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末食棕,一起剝皮案震驚了整個(gè)濱河市朗和,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌簿晓,老刑警劉巖眶拉,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異憔儿,居然都是意外死亡忆植,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門谒臼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來朝刊,“玉大人,你說我怎么就攤上這事蜈缤∈懊ィ” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵底哥,是天一觀的道長痪枫。 經(jīng)常有香客問我织堂,道長,這世上最難降的妖魔是什么奶陈? 我笑而不...
    開封第一講書人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任易阳,我火速辦了婚禮,結(jié)果婚禮上吃粒,老公的妹妹穿的比我還像新娘潦俺。我一直安慰自己,他們只是感情好徐勃,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開白布事示。 她就那樣靜靜地躺著,像睡著了一般僻肖。 火紅的嫁衣襯著肌膚如雪肖爵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,785評(píng)論 1 314
  • 那天臀脏,我揣著相機(jī)與錄音劝堪,去河邊找鬼。 笑死揉稚,一個(gè)胖子當(dāng)著我的面吹牛秒啦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播搀玖,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼余境,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了灌诅?” 一聲冷哼從身側(cè)響起芳来,我...
    開封第一講書人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猜拾,沒想到半個(gè)月后绣张,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡关带,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年侥涵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宋雏。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡芜飘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出磨总,到底是詐尸還是另有隱情嗦明,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布蚪燕,位于F島的核電站娶牌,受9級(jí)特大地震影響奔浅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诗良,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一汹桦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鉴裹,春花似錦舞骆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至总处,卻和暖如春狈惫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹦马。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來泰國打工胧谈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人菠红。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像难菌,于是被迫代替她去往敵國和親试溯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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