做用域分爲全局做用域和局部做用域,對應的變量爲全局變量和局部變量函數
全局做用域是JavaScript程序執行時,系統在內存中保留一塊全局變量的區域性能
局部做用域爲函數執行時產生,局部變量只有在函數內部能夠訪問和更改,因此又叫函數級變量spa
var a = 0; function myfunc (){ var b = 1; c = 2; }
如以上代碼,a爲全局變量,而b爲局部變量,c的話並無用var聲名就直接賦值,此爲暗示全局變量,myfunc若是沒有被執行的話,c就是undefined,執行myfunc函數後,c爲全局變量,能夠在全局做用域訪問和修改,var d = e = 3; 其中e也是暗示全局變量code
能夠將做用域的概念簡單的理解爲上圖,黑色的圓圈爲城牆,城裏的人想要一個東西,就先在城裏找,城裏沒有就出城找,注意不能出去以後不能再進別的城,只能一直往出走,若是最外面(全局)也沒有的話,證實沒有被定義,報錯,元素 id not undefined,(這個是報錯,和返回undefined不同);而城外的想要找對象就只能往出找,不能進城,即便城裏有。對象
做用域鏈就是一層一層的往出走時,每一層的做用域。blog
在JavaScript的Function對象中,有一個不能被訪問的內部屬性,叫作[[Scope]],它裏面就包含了這個函數被建立的做用域中對象的集合,也就是做用域鏈。ip
函數剛建立尚未被運行時,他的做用域鏈就被創建,做用域鏈的最底層必定是Global Object(全局對象),其上多是一層一層的函數做用域,也可能沒有。內存
在執行函數時會產生一個名爲運行期上下文的內部對象(Activation Object(活動對象)),而且加到做用域鏈的最上面,這裏還有一個預編譯的事情,寫在最後。作用域
做用域鏈對性能是有影響的:it
舉個栗子:有一天,你在家裏想看蒙娜麗莎,因此你開始在家裏找,沒找到,因而你就在整個縣城找,仍是沒有,你有去了市裏,省裏,全國,都沒有找到,最後只能出國終於在法國找到了。
這個過程就是JavaScript運行時遇到的問題,他須要一層一層的往外找,而訪問全局(出國)是最慢的,在寫代碼是就常常會遇到這個問題,若是不注意,慢慢積累就會對性能產生很大的影響。
好比最常引用的全局變量document,若是一個函數老是用它,每次用都有遍歷整個做用域鏈,就至關於找了一回蒙娜麗莎,因此應該避免這樣,最簡單的方法就是在函數內部var doc = document事先就把document找到,這樣只須要找一次就能夠了,就至關於把「蒙娜麗莎」畫在了小本本上面,每次想看就看一眼小本本,省了屢次出國,只出去看一次,畫下來就好。
除了這個方法還有其餘的方法:
用with語句也能夠,不過會有一個問題,with改變了做用域鏈,它在做用域鏈的最上面加了一個對象,with就好像在你想看蒙娜麗莎的時候,with就在你耳邊悄悄的說「羅浮宮」,你立馬就去了羅浮宮看來蒙娜麗莎,聽起來是快了,可是下一次,又想看齊白石的蝦了,它仍是和你說「羅浮宮」,你跑到羅浮宮,沒有,就又回家開始慢慢找,效率更慢了,因此最好不要使用。
還有try catch語句,異常對象拋出語句,try catch會先運行try裏面的語句,若是出現異常,就把異常的對象拋給catch,這樣的話在catch語句中只運行一個函數,這樣就沒有局部變量的訪問,做用域鏈的臨時改變就不會影響代碼。固然,前提是瞭解可能出現的錯誤。
最好仍是使用var doc = document,簡單直接,乾淨利落
預編譯:
瞭解函數預編譯過程能夠解決函數的不少問題
預編譯發生在函數執行的前一刻,主要是爲了提高聲明
有的人是記函數聲明提高大於變量提高,但我不推薦這樣記,由於最好是按計算機的步驟來走,分四步
一、建立AO(活動對象)
二、找到形參和變量聲明,將變量和形參做爲AO屬性名(變量聲明提高),不賦值,值爲undefined
三、實參形參相統一(將實參賦給形參)
四、在函數體裏找函數聲明,值賦予函數體(函數聲明提高)
這就是函數的預編譯過程,函數變量聲明提高大是由於它走的晚,若是和變量名相同,會有覆蓋
全局的預編譯同理
先建立GO(==window),變量聲明提高,函數聲明提高
注:以上知識點爲觀看《高性能JavaScript》後根據其內容加上本身的理解寫的,寫的很差、看不懂的地方,能夠去看原著