作用域是什么
編譯原理
JavaScript 常常被稱為‘弱類型’或者‘動態(tài)’語言,實際上它也是一門編譯語言。
與傳統(tǒng)編譯語言不同,它不是提前編譯的。
傳統(tǒng)編譯語言流程:
- 分詞/詞法分析
將由字符組成的字符串分解成有意義的代碼塊(詞法單元) - 解析/語法分析
將詞法單元流轉換成“抽象語法樹”(AST) - 代碼生成
將AST轉換成可執(zhí)行代碼
但是對于 JavaScript 來說編譯一般發(fā)生在代碼執(zhí)行前的極短時間內(nèi)甫贯,JS 引擎也想盡了一切辦法對性能進行優(yōu)化。
作用域
作用域是根據(jù)名稱查找變量的一套規(guī)則看蚜,決定了當前執(zhí)行的代碼可以訪問哪些標識符(變量)叫搁。
如果訪問標識符的目的是為其賦值,會進行 LHS 查詢(a = 1
);如果目的是獲取變量的值常熙,會進行 RHS 查詢(b = a
)纬乍。
作用域嵌套
當一個塊或函數(shù)嵌套在另一個塊或函數(shù)中時,就發(fā)生了作用域嵌套裸卫。
在當前作用域中找不到的某個變量仿贬,會在其外部作用域中查找,直到全局作用域為止墓贿,在瀏覽器中全局作用域為 window 對象茧泪。
詞法作用域
詞法階段
簡單說,詞法作用域是定義在詞法階段的作用域聋袋。
換句話說队伟,詞法作用域是由代碼寫在哪里決定的,所以函數(shù)的詞法作用域決定于函數(shù)聲明的位置幽勒,不被運行時的所處的作用域位置改變嗜侮。
作用域查找會在找到第一個匹配的標識符后停止,外層嵌套作用域嵌套的同名標識符會被忽略啥容。
欺騙詞法
有兩種機制可以在運行時修改詞法作用域锈颗,eval()
和with
操作符,但是會產(chǎn)生難以理解的代碼咪惠、降低性能击吱、某些時候帶來安全隱患等問題,所以早已經(jīng) 禁止使用遥昧。
函數(shù)作用域和塊作用域
函數(shù)作用域
函數(shù)作用域是指函數(shù)內(nèi)包括參數(shù)的全部變量都可以在整個函數(shù)內(nèi)部使用覆醇,當然也包含內(nèi)部的嵌套作用域內(nèi)。
隱藏內(nèi)部實現(xiàn)
函數(shù)作用域內(nèi)的變量和函數(shù)被 隱藏 了起來炭臭∮琅В可以用這個特性生成函數(shù)的私有變量和私有函數(shù)。
另一個好處是可以避免同名標識符的沖突鞋仍,畢竟標識符查找到第一個匹配就會停止憨奸。
塊作用域
感謝 ES6 帶來的 let
和 const
,實現(xiàn)塊級作用域更加的方便凿试。
提升
JavaScript 代碼在很多情況下是從上到下一行一行執(zhí)行的,但是在某些特殊情況下不是似芝。
var a =1; //實際上引擎是這樣來執(zhí)行的 var a; a = 1;
先聲明后賦值才是背后的流程那婉。在每個作用域內(nèi),申明提升都會各自存在党瓮。注意 let
和 const
并沒有申明提升详炬。
函數(shù)優(yōu)先
還是那句話,函數(shù)是一等公民。函數(shù)的聲明同樣會提升呛谜,并且會提升在變量之前在跳。
作用域閉包
閉包是基于詞法作用域書寫代碼的自然結果,不需刻意使用隐岛。
當函數(shù)可以記住并訪問所在詞法作用域猫妙,并可以在當前詞法作用域之外執(zhí)行,就產(chǎn)生了閉包聚凹。
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2