現(xiàn)在該講作用域鏈了。在本文中乘盼,我假設(shè)你了解執(zhí)行上下文的基礎(chǔ)知識(shí):盡管如此兔跌,我也會(huì)很快就此發(fā)表一篇文章。??
我們來(lái)看看如下代碼:
const name = "Lydia"
const age = 21
const city = "San Francisco"
function getPersonInfo() {
const name = "Sarah"
const age = 22
return `${name} is ${age} and lives in ${city}`
}
console.log(getPersonInfo())
我們?cè)谡{(diào)用getPersonInfo()
函數(shù)婉商,該函數(shù)返回一個(gè)字符串,其中包含name
渣叛、age
和city
變量的值:Sarah is 22 and lives in San Francisco
丈秩。不過(guò),getPersonInfo()
函數(shù)并沒(méi)有包含名為city
的變量淳衙,它是如何知道city
的值的呢蘑秽?
首先饺著,內(nèi)存空間是為不同的上下文設(shè)置的。我們有默認(rèn)的全局上下文(global context)(在瀏覽器中是window
肠牲,在Node中是global
)幼衰,以及針對(duì)已被調(diào)用的getPersonInfo()
函數(shù)的本地上下文(local context)。每個(gè)上下文還有一個(gè)作用域鏈(scope chain)缀雳。
對(duì)于getPersonInfo()
函數(shù)渡嚣,作用域鏈看起來(lái)像這樣(不要擔(dān)心,現(xiàn)在還不需要完全搞清楚):
作用域鏈基本上是對(duì)對(duì)象的“引用鏈”肥印,這些對(duì)象包含對(duì)在該執(zhí)行上下文中可引用的值(和其他作用域)的引用识椰。(?:“嘿,這些都是你可以在此執(zhí)行上下文中引用的所有值”)深碱。作用域鏈?zhǔn)窃趧?chuàng)建執(zhí)行上下文時(shí)創(chuàng)建的腹鹉,這意味著它是在運(yùn)行時(shí)創(chuàng)建的!
但是莹痢,在本文中种蘸,我一般不會(huì)討論活動(dòng)對(duì)象(Activation Object)或執(zhí)行上下文(execution context),我們只關(guān)注作用域竞膳!在如下的示例中航瞭,執(zhí)行上下文中的鍵/值對(duì)表示作用域鏈中含有的對(duì)變量的引用。
全局執(zhí)行上下文的作用域鏈有對(duì)3個(gè)變量的引用:值為Lydia
的name
坦辟,值為21
的age
刊侯,以及值為San Francisco
的city
。在本地執(zhí)行上下文中锉走,有對(duì)2個(gè)變量的引用:值為Sarah
的name
滨彻,以及值為22
的age
。
當(dāng)我們?cè)噲D訪問(wèn)getPersonInfo()
函數(shù)中的變量時(shí)挪蹭,引擎會(huì)首先檢查本地作用域鏈亭饵。
本地作用域鏈中有對(duì)name
和age
的引用!name
的值為Sarah
梁厉,age
的值為22
辜羊。但是現(xiàn)在,試圖訪問(wèn)city
時(shí)候會(huì)發(fā)生什么词顾?
為了找到city
的值八秃,引擎會(huì)沿著作用域鏈向下找。這基本上只是意味著引擎不會(huì)輕易放棄:它會(huì)努力為你看看能否在本地作用域引用的外層作用域中找到變量city
的值肉盹,在本例中昔驱,外層作用域是global對(duì)象。
在全局執(zhí)行上下文中上忍,我們聲明了變量city
骤肛,其值為San Francisco
纳本,因此全局執(zhí)行上下文中有一個(gè)對(duì)變量city
的引用。現(xiàn)在我們有了該變量的值腋颠,函數(shù)getPersonInfo()
就可以返回字符串Sarah is 22 and lives in San Francisco
??饮醇。
我們可以沿著作用域鏈向下找,但是不能沿著作用域鏈向上找秕豫。好吧,這可能會(huì)令人困惑观蓄,因?yàn)橛腥苏f(shuō)的是向上而不是向下混移,所以我要重新表述一下:向外層作用域方向找,而不是向更內(nèi)層作用域方向找侮穿。我喜歡將這用圖形表示為一種瀑布:
甚至更深:
下面我們以這段代碼為示例歌径。
代碼幾乎是一樣的,不過(guò)有一個(gè)很大的不同點(diǎn):現(xiàn)在我們只在getPersonInfo()
函數(shù)中聲明了city
變量亲茅,但在全局作用域中沒(méi)有聲明回铛。我們沒(méi)有調(diào)用getPersonInfo()
函數(shù),因此也沒(méi)有創(chuàng)建本地執(zhí)行上下文克锣。但是茵肃,我們?cè)噲D在全局執(zhí)行上下文中訪問(wèn)name
、age
和city
的值袭祟。
然后它就拋出了一個(gè)ReferenceError
錯(cuò)誤验残!在全局作用域中找不到一個(gè)對(duì)變量city
的引用,也沒(méi)有可以查找的外層作用域巾乳,并且它不能沿著作用域向上查找您没。
這樣,我們就可以把作用域作為保護(hù)變量并重用變量名的一種方法胆绊。
除了全局和本地作用域氨鹏,還有一個(gè)塊作用域。用let
或者const
關(guān)鍵字聲明的變量的作用域?yàn)樽罱咏拇罄ㄌ?hào)({``}
)压状。
const age = 21
function checkAge() {
if (age < 21) {
const message = "You cannot drink!"
return message
} else {
const message = "You can drink!"
return message
}
}
可以把作用域用圖形表示為:
這里我們有一個(gè)全局作用域仆抵,一個(gè)函數(shù)作用域和兩個(gè)塊作用域。我們能兩次聲明變量message
何缓,因?yàn)樵撟兞康淖饔糜蚍秶谴罄ㄌ?hào)內(nèi)肢础。
下面快速回顧一下:
- 可以把作用域鏈看作是對(duì)我們可以在當(dāng)前執(zhí)行上下文中可以訪問(wèn)的值的一個(gè)引用鏈。
- 作用域還讓重用在作用域鏈向下更深層次定義的變量名成為可能碌廓,因?yàn)樽兞棵荒苎刂饔糜蛳蛳抡掖洌荒芟蛏险摇?/li>
關(guān)于作用域和作用域鏈就寫(xiě)到這里了!
原文 by Lydia Hallie:https://dev.to/lydiahallie/javascript-visualized-scope-chain-13pd