所謂的作用域,一句話概括就是:變量在聲明它們的函數體以及這個函數體內嵌套任何的函數體內都是有定義的。
由於函數本身也是一個變量,所以它也有作用域,函數的作用域和變量的作用域一樣,也是聲明其定義時所在的作用域,與運行時無關,即函數的作用域在函數體內的變量聲明之前就可用了。
JS不像其他語言,在JS中,if、while、for等代碼塊是不能形成獨立的塊級作用域,所謂的塊級作用域是指一對花括號裏面的每一段代碼都有獨立的作用域,外界是訪問不到的,但JS並不是,在JS中,同一個函數體內,如果裏面含有if、while、for等語句,那麼函數體內的變量是可以訪問到這些語句裏面的,但是在這個函數的外部卻是訪問不到函數內部的變量的,此時變量又分爲全局變量和局部變量:
全局變量:全局變量是指整個程序的任意一行都可以引用的變量。由於它的作用域是整個程序,所以又稱爲全局變量作用域。
局部變量:局部變量是指定義在函數內部的變量。由於它的作用域是局部性的,所以又稱爲局部變量作用域。
在JavaScript中,函數內部可以直接讀取全局變量,而函數外部是無法直接讀取函數內部的局部變量的。
例1:
var a=1;
function f1(){
console.log(a);
}f1();
輸出結果爲:1
例2:
function f2(){
var a=1;
}
console.log(a);
輸出結果爲:”a is notdefined”。(即a未定義,這是爲什麼呢?這是因爲此時是在函數外面,所以是無法訪問函數內部的局部變量a的)
例3:
function f3(){
a=1;
console.log(a);
}f3();
輸出結果爲:1
(爲什麼例3中的結果爲1?因爲沒有對a進行var聲明,所以它是一個全局變量,所以即便它是在函數內部,在函數外部也是能讀取它!)
變量提升:在提到變量作用域時,有一個詞不得不提一下,那就是變量提升,什麼是變量提升呢?舉個例子:
例:
var a=1;
function test(){
console,log(a);
}test();
通過前面的瞭解,我們都知道這個例子運行的結果是1.
Ok,那麼再舉個例子:
var a=1;
function test(){
console.log(a);
var a=2;
}test();
那這個例子的運行結果是什麼呢?答案是undefined。這是爲什麼呢?這就是上面提到的變量提升,其實上面的例子運行的過程這樣的:
var a=1;
function test(){
var a;
console.log(a);
a=2;
}test();
所以纔會undefined。也就是說,函數中變量的作用域是在函數定義時確定的,也就是在預處理過程中完成的,而變量的值是在函數調用的時候才確定下來的,test()在運行時只會去確認它的值,所以要區分好函數的定義和調用,兩者是不一樣的函數執行時候的環境是定義時的作用域,而不是調用時所在的作用域。
執行環境:首先需要注意一點的是,執行環境和作用域是兩個完全不同的概念。
執行環境也稱執行上下文,函數每次調用都有與之緊密相關的作用域和執行環境,從根本上說,作用域是基於函數的,而執行環境是基於對象的。作用域涉及到所被調用的變量訪問,並且不同的調用場景是不一樣的。而對於執行環境而言,每個執行環境都有一個與之相關聯的變量對象,環境中所有定義的變量和函數都保持在這個對象當中,執行環境用於當前所執行代碼的對象的引用。
執行環境分爲創建和執行兩個階段,在創建階段,解析器首先會創建一個變量對象,它由定義在函數中的聲變量、函數聲明以及參數組成。然後在執行階段進行代碼的執行。
我們可以通過解析器工作的過程去更好的理解執行環境:當JS代碼被瀏覽器載入時,默認最先進入的是一個全局的環境,當在全局環境中調用一個函數時,程序流就會進入到該函數內,此時JS引擎就會爲該函數創建一個新的執行環境(可以理解爲局部執行環境),並且將其壓入到執行環境堆棧的頂部。由於瀏覽器總是會執行當前堆棧頂部的執行環境,一旦執行完畢,該執行環境就會被彈出,然後進入其下的執行環境執行代碼,這樣,堆棧中的執行環境就會依次被執行並且彈出堆棧,直到回到全局的執行環境。