執行JavaScript時,接收到翻譯的電腦會先創造一個全域執行環境。當程式呼叫函式,就會在全域環境中創造該函式的(區域)執行環境。而每個執行環境不論全域或區域,都有屬於自己的變數作用域存在。
來看看以下程式碼:
function b() {
var myVar;
console.log(myVar);
}
function a() {
var myVar = 2;
console.log(myVar);
b();
}
var myVar = 1;
console.log(myVar);
a();
這樣會顯示什麼呢?
在創造與執行階段,發生了什麼事?我們按照順序來看:
console.log(myVar);
印出1。console.log
印出函式a的變數myVar 顯示是2。console.log
印出函式b的變數myVar,顯示是undefined
(因為我們沒賦值給它)。所以雖然myVar被宣告了3次,但因為它都在不同的執行環境中,所以這3個其實是不同的變數。
如果我們在剛剛的程式a();
下面再加一行console.log(myVar);
執行後回發現重新印出myVar仍然顯示1,因為這個myVar就是原本全域的變數myVar,代表其實他沒有被覆蓋掉。
如果函式裡的變數宣告沒有使用var呢?
看看以下程式碼
function a() {
var myVar = 2;
b();
}
function b() {
console.log(myVar);
}
var myVar = 1;
a();
執行 結果是
console.log(myVar);
這段程式碼是在function b中
b這個函式中根本沒有宣告變數
那為何會印出1而非undefined
?
當函式中使用到沒有定義的變數時,JS會接著到外部環境尋找(外部參照),注意這裡的外部環境不是指呼叫這個函式的執行環境,而是指定義宣告這個函式的外部環境。
函式b是在全域環境定義,在函式a中呼叫執行,既然是向定義環境找,所以函數b的外部環境就是全域環境,而不是函式a,故它就找到全域環境的變數myVar,最後印出1。
再看看以下程式碼
function a() {
function b() {
console.log(myVar);
}
var myVar = 2;
b();
}
var myVar = 1;
a();
b();
這樣會顯示什麼呢?
為何b會報錯?
因為全域執行環境找不到function b,function b是被定義在function a的環境之中,而全域執行環境只有看到function a的定義存在。
那為何一開始又會先顯示2呢?
當外部環境呼叫執行function a,a裏頭宣告了變數myVar賦值2,再呼叫執行function b,此時執行function b裡的console.log(myVar)
,因為b裡面沒有宣告myVar這個變數存在,於是便往外部環境查找,也就是外部定義的環境,b是定義在function a中,所以找到的自然是function a的myVar,顯示印出2囉。
上述筆記了這麼多,那有沒有強調區域性的宣告方式?
讓變數在使用不會這麼複雜,沒宣告就會報錯,而不是外部查找。
還是有的!那就是使用ES6新增的let宣告!
let具有區塊範圍(block scoping)特性,用let宣告的相同變數名稱,在全域、區域、不同執行環境彼此互不影響。
小結
今天我們知道全域執行環境就有全域變數、區域執行環境就有區域變數,並且知道當區域內找不到變數時會向外查找,向外查找的外部環境是指外部定義的環境,不能直接認定就是全域環境與呼叫的環境。
今天的筆記內容可以參照Udemy課程:JavaScript 全攻略:克服JS 的奇怪部分2-14~2-16