在前一篇文章中,咱們把做用域定義爲」管理、維護變量的一套規則」,接下來是時候來深刻討論一下Js的做用域問題了,首先咱們要知道做用域通常有兩種主要的工做類型,一種是詞法做用域,一種是動態做用域, Javascript採用的是詞法做用域, 關於動態做用域的有興趣的能夠自行Google。函數
1.詞法階段性能
首先咱們要理解」詞法階段」這個詞語,咱們已經瞭解到Js存在一個編譯階段,編譯階段的第一步就是分詞/詞法分析,咱們能夠簡稱爲」詞法階段」優化
簡單來講,詞法做用域就是定義在詞法階段的做用域,詞法做用域是你在寫代碼時把變量和塊做用域寫在哪裏來決定的,詞法分析器處理代碼後,在大部分狀況下會保持做用域不變。咱們須要注意如下幾點:對象
a.當引擎須要查詢變量時,老是從當前做用域開始查找ip
b.做用域查找會在找到第一個匹配的標識符時中止作用域
c.遮蔽效益(內部的標識符」遮蔽」了外部的標識符)字符串
d.全局變量會自動成爲全局對象的屬性,經過這種技術能夠間接的訪問那些被遮蔽的全局變量it
e.不管函數在哪裏被調用,也不管它如何被調用,它的詞法做用域也只由它被聲明的位置決定io
f.詞法做用域之會查找一級標識符,好比a、b、c。若是代碼中引用了foo.bar.baz,詞法做用域只會試圖查找foo標識符,而後在使用對象屬性訪問規則進行對bar以及baz的訪問編譯
2.欺騙詞法
咱們有時可使用一些語句對詞法做用域進行欺騙,可是要注意的一點:欺騙詞法做用域會致使性能降低
2.1 eval()
eval() 接受一個字符串參數,將這段字符串視做Javascript執行
在嚴格模式下,eval()中的代碼有本身的詞法做用域,所以其中的聲明沒法修改做用域外的代碼的效果,而在非嚴格模式下,eval()中的代碼能夠修改eval()方法所在的做用域,即eval()方法中的全部聲明與eval()方法處於同一個詞法做用域,於是能夠修改最終的效果。
new Function(...) 相似,將對傳入的字符串動態生成函數,所以不做過多闡述。
2.2 with()
with(obj){ … } 實質上是建立了或者指向了obj中的詞法做用域,在這個做用域中,全部的聲明在被引擎執行時,都會在這個做用域中查找,若是查找不到,會在obj的上一層做用域繼續查找,可是,當若是一直到頂層的全局做用域尚未找到時,會建立一個全局變量。而且,with在嚴格模式下被徹底禁止運行。
2.3 性能
eval() 和 with() 會在運行時修改或建立做用域,以此來欺騙其餘詞法做用域。可是會極大的下降代碼的運行效率,有可能以前對代碼進行的優化會所有無效,使得代碼的運行變得很慢。