看完記憶體儲存差異,現在要來談談全域污染這件事。
所謂的範疇Scope是規範變數有效的區域
在JavaScript內有三個主要的變數有效範疇:
透過下面這段程式碼來標示所謂的scope:
let x = 2;
let y = 2;
function trys(){
let x = 3;
let y = 3;
}
{
let x = 4;
let y = 4;
}
不同的宣告有不同的變數有效範疇,以下簡單以表格呈現:
宣告方式 | var |
let |
const |
---|---|---|---|
有效區域 | global scope / function scope | global scope / funciotn scope / block scope | global scope / funciotn scope / block scope |
有效區域指的是什麼意思,也直接看例子吧?
//區域範疇內有x變數
{
let x = 3;
}
console.log(x);
可以理解成x
這個宣告,出了這個區域就會無效(無法取得)。
其實之所以會出現所謂scope的現象,背後是由字彙環境一手包辦的,什麼意思?
字彙環境(lexical environment)的任務就是:追蹤紀錄範疇內(scope)的識別字
let x = 2;
let y = 2;
function trys(){
let x = 3;
let y = 3;
}
{
let x = 4;
let y = 4;
}
會透過字彙環境紀錄自己區域內的宣告內容,在你需要使用時就會來找尋
當你要找尋的變數,不存在當前的字彙環境中,它會往上層的字彙環境找尋,若到了全域都沒有此變數,則會回報reference error
。
假設我們今天在函式內會取用到z
這個變數,但它不存在:
let z = "我在全域";
function doIt(i){
console.log(z);
}
doIt();
最後由圖可知,變數z
會在全域找到。
理念大概理解,用實例來試試看吧
for
迴圈結構!來看一個經典的範疇(scope)相關題目
var
宣告時for(var i =0;i <4;i++){
setTimeout(function(){
console.log(i,"我是結果");
},1000)
}
你想像的執行過程跟的結果
i = 0; console.log(0,"我是結果");
i = 1; console.log(1,"我是結果");
i = 2; console.log(2,"我是結果");
i = 3; console.log(3,"我是結果");
i = 4; console.log(4,"我是結果");
//0,"我是結果";
//1,"我是結果";
//2,"我是結果";
//3,"我是結果";
//4,"我是結果";
實際上的結果
因為
var
跟let
的差別前面有提過兩者的範疇上的差異:
用var
時,因為它不具有block scope,所以並不存在{}
的字彙環境,因此不會紀錄i
的值,所以會往global environment尋找此值,
i = 0;
i = 1;
i = 2;
i = 3;
i = 4;
console.log(4);
console.log(4);
console.log(4);
console.log(4);
console.log(4);
而因為非同步的執行,所以當要印出所有i
時,global environment所紀錄的值,已經是迭代完成的值,也就是4
。
那為何let
就可以?
前面提過let
具有block environment的特性,所以每次迭代時,都會在{}
建立字彙環境,並記住當前i
的值。
所以你可以想像為:
//開始迭代: for environment
{
let i =0;
console.log(i);
}
//第一圈後: for environment
{
let i =1;
console.log(i);
}
...
{
let i = 4;
console.log(i);
}
不過上述的論述,只是在學習過程與前輩跟同學討論的整理,並沒有在規範中明確定義。
-- to be continued --
那今天就到這邊摟!
每天的休息,是為了後面的追求,明天見。