舉個例子,汽車,絕對是個非常有價值的stuff,它給我們的日常出行,貨物運輸等帶來了極大的便利;筷子,同樣也是個非常有價值的stuff,它給我們吃飯帶來了極大的方便。但是,汽車能幫我們把菜送到嘴裏嗎?筷子能載着我們出行嗎?
那麼,我上面所說的某些領域,我們是不是可以稱其爲作用域,我想是可以的。
說到這,那麼我就想問了:在JS裏,作用域是不是也是類似的概念呢?
首先,我可以肯定的說這是一個在JavaScript中灰常灰常重要的概念,關係着JS裏很多核心的機制,理解它,很多問題都迎刃而解了。
那麼,問問自己,在JS裏,作用域是什麼?
心裏大概知道是什麼,但是細細一想又好像說不太清。
沒關係,下面我們就細細品味這個有意思的東東。
先throw概念吧:
作用域負責收集並維護由所有聲明的標識符(變量)組成的一系列查詢,並實施一套非常嚴格的規則,確定當前執行的代碼對這些標識符的訪問權限。
通俗來說,作用域相當於一個管理員(有自己的一套規則),他負責管理所有聲明的標識符的有序查詢。
我們來講個故事,說說作用域到底幹了啥。
三兄弟齊上陣
long long ago,有3個關係很好的基友,老大叫引擎,老二叫編輯器,老三叫作用域。三兄弟眼看年歲已長,可手上還是沒有幾個銀子。個個都很着急,於是三兄弟謀劃一同做個事。
求職過程:此粗略去數萬個字。。。
最終他們做的工作是:負責JS的編譯和運行。
他們的工作內容是這樣的:
var a = 1;
console.log( a );
開始工作:
- 編譯器:作用域,幫我看看你那有沒有儲存變量a。
作用域:二哥,還沒有。
編譯器:那好,幫我儲存一個。
引擎: 老三,你那有沒有一個叫做a的變量。
編譯器:大哥,還真有,剛二哥讓我存儲了一個。
引擎: 真是太好了,幫我拿出來,它的值是幾,我需要給它複製。
編譯器:大哥,它的值是2。
引擎: 謝謝你,三弟,這樣我就能打印它的值了。
上面講了一個不恰當的小故事,但是三者之間的關係大概就是這樣。
徹底搞懂JavaScript作用域裏介紹過,大部分標準語言編譯器的第一個工作階段叫作詞法化(也叫單詞化)。回憶一下,詞法化的過程會對源代碼中的字符進行檢查,如果是有狀態的解析過程,還會賦予單詞語義。
在JS裏,使用的作用域就是詞法作用域。
簡單地說,詞法作用域就是定義在詞法階段的作用域。換句話說,詞法作用域是由你在寫代碼時將變量和塊作用域寫在哪裏來決定的,因此當詞法分析器處理代碼時會保持作用域不變(大部分情況下是這樣的)。
var a = 1;
function fn() {
var a = 2;
console.log( this.a );
}
fn(); // 1
從上面的代碼,我們可以看出:fn中打印a的值不是由寫代碼的位置確定的,而是取決於fn執行的位置。
區別
詞法作用域是在寫代碼或者說定義時確定的,而動態作用域是在運行時確定的。(this 也是!)
詞法作用域關注函數在何處聲明,而動態作用域關注函數從何處調用。
函數作用域
JS裏,生成作用域的方式:
函數
with、eval (不建議使用,影響性能)
由此,我們知道JS裏,絕大多數的作用域都是基於函數生成的。
每個函數都會爲自身生成一個作用域氣泡。這個氣泡內所有的標識符都可以在這個氣泡中使用。
function bar() {
var a = 1;
function fn() {
var b = 2;
console.log(b);
}
fn();
console.log(a);
}
bar();
上面代碼,bar氣泡有標識符a、fn,因此在bar氣泡中可以訪問到a、fn; fn氣泡有標識符b,因此在bar氣泡中可以訪問到b; 當然還有一個全局氣泡,全局氣泡中有bar標識符,因此在全局氣泡中可以訪問到bar。