作用域
一套設(shè)計(jì)良好的規(guī)則來存儲(chǔ)變量遏匆,并且方便之后再找到變量。這套規(guī)則叫做作用域欣喧。
1. 編譯原理
通常認(rèn)為JavaScript是“動(dòng)態(tài)”||“解釋執(zhí)行”的語(yǔ)言折砸,事實(shí)上是一門編譯語(yǔ)言。不過其并非提前編譯的诊胞,編譯的結(jié)果也不能再分布式系統(tǒng)中移植運(yùn)行暖夭。
JavaScript引擎編譯步驟
- 分詞/詞法分析
將字符串分解為(對(duì)編程語(yǔ)言來說)有意義的代碼塊(即詞法單元) - 解析/語(yǔ)法分析
將詞法單元流(數(shù)組)轉(zhuǎn)換成“抽象語(yǔ)法樹”(AST) - 代碼生成
將AST轉(zhuǎn)換為可執(zhí)行代碼的過程為代碼生成
簡(jiǎn)單來說就是有某種方法可以將
var a = 2;
的AST 轉(zhuǎn)化為一組機(jī)器指令,用來創(chuàng)建一個(gè)叫作a 的變量(包括分配內(nèi)存等)撵孤,并將一個(gè)值儲(chǔ)存在a 中迈着。
任何JavaScript代碼片段在執(zhí)行前都要編譯,大多是發(fā)生在執(zhí)行前幾微秒邪码。
2. 理解作用域
- 引擎
從頭到尾負(fù)責(zé)整個(gè)JavaScript程序的編譯及執(zhí)行過程 - 編譯器
輔助詞法分析&代碼生成 - 作用域
負(fù)責(zé)收集&維護(hù)所有聲明的標(biāo)識(shí)符(變量)組成的一系列查詢裕菠,并實(shí)施非常嚴(yán)格的規(guī)則,確定當(dāng)前執(zhí)行代碼對(duì)這些變量的訪問權(quán)限闭专。
對(duì)var a = 2;
的分解
- 編譯器先將這段代碼分解為詞法單元奴潘,再將詞法單元解析成一個(gè)樹結(jié)構(gòu)
- 遇到
var a
,編譯器會(huì)詢問作用域是否存在同名變量影钉。是->忽略該聲明画髓,繼續(xù)編譯;否->在當(dāng)前作用域的集合中聲明一個(gè)新的變量平委,并命名為a - 接下來奈虾,編譯器會(huì)為引擎生成運(yùn)行時(shí)所需代碼,用來處理
a = 2
的操作廉赔。引擎運(yùn)行時(shí)會(huì)先詢問作用域愚墓,在當(dāng)前作用域集合中是否存在叫a的變量,是->引擎使用此變量昂勉;否->引擎繼續(xù)查找;如未找到扫腺,會(huì)拋出異常
總結(jié):變量的賦值會(huì)執(zhí)行兩個(gè)動(dòng)作岗照,首先在當(dāng)前作用域中聲明變量(如之前未聲明過),然后運(yùn)行時(shí),引擎在作用域中查找該變量攒至,如果找到則進(jìn)行賦值厚者。
LHS查詢&RHS查詢
根據(jù)變量在賦值操作(=
)的左右側(cè)分為,LHS和RHS
- RHS是取值迫吐,查找源值
- LHS是取容器库菲,找到目標(biāo)
函數(shù)聲明function foo(a) {...
不是LHS查詢
而var foo、 foo = function(a) {...
是LHS查詢
function foo(a) {
var b = a;
return a + b;
}
var c = foo( 2 );
這其中有3處LHS和4出RHS
LHS: c=... | a=... | b=...
RHS: foo(2) | =a | a+ | +b
3. 作用域嵌套
在當(dāng)前作用域中無法找到某個(gè)變量志膀,就會(huì)向外層作用域查找熙宇,直到找到該變量或抵達(dá)最外層的作用域(全局作用域)
4. 異常
ES5中引入了“嚴(yán)格模式”,在正常模式|“寬松模式“|”懶惰模式”下溉浙,如果找到全局作用域還是找不到變量烫止,會(huì)自動(dòng)或隱式地創(chuàng)建全局變量。
而在“嚴(yán)格模式”下戳稽,不會(huì)創(chuàng)建并返回一個(gè)全局變量馆蠕,引擎會(huì)拋出ReferenceError異常。
嚴(yán)格模式下惊奇,禁止自動(dòng)或隱式地創(chuàng)建全局變量
小結(jié)
- 作用域是一套規(guī)則互躬,用于確定在何處以及如何查找變量(標(biāo)識(shí)符)
- 查找的目的是對(duì)變量賦值,則是LHS查詢颂郎;目的是獲取變量的值則是RHS查詢